diff --git a/src/components/BPMNPreview.css b/src/components/BPMNPreview.css index d52308d89..a1b5f5e4e 100644 --- a/src/components/BPMNPreview.css +++ b/src/components/BPMNPreview.css @@ -12,7 +12,9 @@ .preview-container { width: 100%; height: 100%; + min-height: 160px; background: var(--bpmn-preview-bg); + position: relative; } .bpmn-preview-svg { diff --git a/src/components/BPMNPreview.js b/src/components/BPMNPreview.js index f6f634a8e..53677dca1 100644 --- a/src/components/BPMNPreview.js +++ b/src/components/BPMNPreview.js @@ -11,7 +11,15 @@ const BPMNPreview = ({ file, repository, selectedBranch, profile }) => { useEffect(() => { const loadPreview = async () => { - if (!file || !repository || !containerRef.current) return; + if (!file || !repository || !containerRef.current) { + console.log('🚫 BPMNPreview: Missing required props:', { + hasFile: !!file, + hasRepository: !!repository, + hasContainer: !!containerRef.current, + fileName: file?.name + }); + return; + } try { setLoading(true); @@ -27,6 +35,16 @@ const BPMNPreview = ({ file, repository, selectedBranch, profile }) => { const ref = selectedBranch || 'main'; const isDemo = file.path?.includes('demo/') || file.sha?.startsWith('demo-'); + console.log('🎬 BPMNPreview: Starting preview load for file:', { + fileName: file.name, + filePath: file.path, + owner: owner, + repoName: repoName, + ref: ref, + isDemo: isDemo, + hasDownloadUrl: !!file.download_url + }); + let bpmnXml; if (isDemo) { @@ -82,12 +100,41 @@ const BPMNPreview = ({ file, repository, selectedBranch, profile }) => { `; } else { // For real files, try to load the actual BPMN content + console.log('📥 BPMNPreview: Attempting to load real BPMN file content...'); try { + console.log('🌐 BPMNPreview: Calling githubService.getFileContent with params:', { + owner, repoName, path: file.path, ref + }); bpmnXml = await githubService.getFileContent(owner, repoName, file.path, ref); + console.log('✅ BPMNPreview: Successfully loaded BPMN content, length:', bpmnXml?.length); + console.log('🔍 BPMNPreview: Content preview (first 100 chars):', bpmnXml?.substring(0, 100)); + + // Validate that we got actual BPMN content + if (!bpmnXml || typeof bpmnXml !== 'string') { + throw new Error('Invalid content received: not a string'); + } + + if (!bpmnXml.includes('bpmn:definitions') && !bpmnXml.includes(' { } } + console.log('🔧 BPMNPreview: Creating BPMN viewer...'); + console.log('🔍 BPMNPreview: About to create viewer with BPMN content length:', bpmnXml?.length); + // Create and initialize viewer with clean separation const viewer = new BpmnViewer(); viewerRef.current = viewer; + + console.log('✅ BPMNPreview: BPMN viewer instance created successfully'); try { - // Attach viewer to container first - await viewer.attachTo(containerRef.current); + console.log('🔗 BPMNPreview: Attaching viewer to container...'); + console.log('🔍 BPMNPreview: Container element details:', { + exists: !!containerRef.current, + className: containerRef.current?.className, + width: containerRef.current?.offsetWidth, + height: containerRef.current?.offsetHeight, + parentExists: !!containerRef.current?.parentElement + }); + + // Create timeout promise for viewer operations + const createTimeoutPromise = (operation, timeoutMs = 10000) => { + return new Promise((_, reject) => { + setTimeout(() => { + reject(new Error(`${operation} timeout after ${timeoutMs}ms`)); + }, timeoutMs); + }); + }; + + // Attach viewer to container first with timeout + const attachPromise = viewer.attachTo(containerRef.current); + await Promise.race([attachPromise, createTimeoutPromise('Viewer attach', 5000)]); + console.log('✅ BPMNPreview: Successfully attached viewer to container'); - // Then import XML - await viewer.importXML(bpmnXml); + console.log('📊 BPMNPreview: Importing BPMN XML...'); + console.log('🔍 BPMNPreview: XML content preview (first 200 chars):', bpmnXml?.substring(0, 200)); + // Import XML with timeout handling + const importStartTime = Date.now(); + const importPromise = viewer.importXML(bpmnXml); + const importResult = await Promise.race([importPromise, createTimeoutPromise('XML import', 15000)]); + const importTime = Date.now() - importStartTime; + + console.log(`✅ BPMNPreview: Successfully imported BPMN XML in ${importTime}ms`); + console.log('📊 BPMNPreview: Import result details:', { + warnings: importResult?.warnings?.length || 0, + hasWarnings: !!(importResult?.warnings?.length), + warningDetails: importResult?.warnings + }); + + if (importResult?.warnings?.length > 0) { + console.warn('⚠️ BPMNPreview: Import warnings:', importResult.warnings); + } + + console.log('🎯 BPMNPreview: Fitting to viewport...'); // Fit to viewport for preview const canvas = viewer.get('canvas'); + console.log('🔍 BPMNPreview: Canvas service retrieved:', !!canvas); + + const zoomStartTime = Date.now(); canvas.zoom('fit-viewport'); + const zoomTime = Date.now() - zoomStartTime; + + console.log(`✅ BPMNPreview: Successfully fitted to viewport in ${zoomTime}ms`); + + // Final validation - check if diagram was actually rendered + const viewbox = canvas.viewbox(); + console.log('🔍 BPMNPreview: Final viewport details:', { + viewbox, + hasElements: viewbox.inner?.width > 0 && viewbox.inner?.height > 0, + containerHasContent: containerRef.current?.children?.length > 0 + }); + + // Check if container actually has content + if (containerRef.current?.children?.length === 0) { + console.warn('⚠️ BPMNPreview: Container is empty after rendering - potential issue'); + } + console.log(`🎉 BPMNPreview: Successfully rendered preview for: ${file.name}`); setLoading(false); } catch (importError) { - console.error('Failed to import BPMN XML:', importError); - setError('Failed to load preview'); + console.error('❌ BPMNPreview: Failed to import BPMN XML:', importError); + console.error('🔍 BPMNPreview: Import error details:', { + message: importError.message, + stack: importError.stack, + fileName: file.name, + xmlLength: bpmnXml?.length, + xmlPreview: bpmnXml?.substring(0, 300), + containerState: { + exists: !!containerRef.current, + hasChildren: containerRef.current?.children?.length || 0, + clientHeight: containerRef.current?.clientHeight, + clientWidth: containerRef.current?.clientWidth + } + }); + setError(`Failed to load preview: ${importError.message}`); setLoading(false); } } catch (renderError) { - console.error('Failed to render BPMN preview:', renderError); + console.error('❌ BPMNPreview: Failed to render BPMN preview:', renderError.message || renderError); + console.log('🔍 BPMNPreview: Error details:', { + fileName: file.name, + filePath: file.path, + errorMessage: renderError.message, + errorStack: renderError.stack + }); setError('Failed to load preview'); setLoading(false); } @@ -182,8 +311,15 @@ const BPMNPreview = ({ file, repository, selectedBranch, profile }) => { // Only run if we have all required props if (file && repository && containerRef.current) { + console.log('🚀 BPMNPreview: Starting loadPreview for:', file.name); loadPreview(); } else { + console.log('⏭️ BPMNPreview: Skipping loadPreview, missing props:', { + hasFile: !!file, + hasRepository: !!repository, + hasContainer: !!containerRef.current, + fileName: file?.name + }); setLoading(false); } diff --git a/src/components/BPMNViewer.js b/src/components/BPMNViewer.js index 560bd171a..38fc49f93 100644 --- a/src/components/BPMNViewer.js +++ b/src/components/BPMNViewer.js @@ -6,6 +6,14 @@ import { PageLayout, useDAKParams } from './framework'; import './BPMNViewer.css'; const BPMNViewerComponent = () => { + return ( + + + + ); +}; + +const BPMNViewerContent = () => { const location = useLocation(); const navigate = useNavigate(); const viewerRef = useRef(null); @@ -475,7 +483,6 @@ const BPMNViewerComponent = () => { } return ( -
@@ -598,7 +605,6 @@ const BPMNViewerComponent = () => {
-
); }; diff --git a/src/components/BusinessProcessSelection.js b/src/components/BusinessProcessSelection.js index bf8420491..139eaf27a 100644 --- a/src/components/BusinessProcessSelection.js +++ b/src/components/BusinessProcessSelection.js @@ -116,6 +116,17 @@ const BusinessProcessSelection = () => { const bpmnFiles = await githubService.getBpmnFiles(owner, repoName, ref); + console.log('📊 BusinessProcessSelection: Received BPMN files:', { + count: bpmnFiles.length, + files: bpmnFiles.map(f => ({ + name: f.name, + path: f.path, + size: f.size, + hasDownloadUrl: !!f.download_url, + sha: f.sha?.substring(0, 8) + })) + }); + // If no files found and we're in demo mode, provide fallback files if (bpmnFiles.length === 0 && profile?.isDemo) { console.log('No BPMN files found in demo mode, providing fallback demo files'); diff --git a/src/components/DAKDashboardWithFramework.js b/src/components/DAKDashboardWithFramework.js index 7135780bb..3df538985 100644 --- a/src/components/DAKDashboardWithFramework.js +++ b/src/components/DAKDashboardWithFramework.js @@ -32,7 +32,7 @@ const DAKDashboardContent = () => { try { // Check repository permissions - const hasPermission = await githubService.checkRepositoryPermissions(repository.owner.login, repository.name); + const hasPermission = await githubService.checkRepositoryWritePermissions(repository.owner.login, repository.name); setHasWriteAccess(hasPermission); } catch (error) { console.error('Error checking repository permissions:', error); @@ -58,7 +58,7 @@ const DAKDashboardContent = () => { } try { - const issues = await githubService.getRepositoryIssues(repository.owner.login, repository.name); + const issues = await githubService.getIssues(repository.owner.login, repository.name); // Count issues by label const counts = {}; diff --git a/src/components/framework/PageProvider.js b/src/components/framework/PageProvider.js index b2a6d6507..fa6278f61 100644 --- a/src/components/framework/PageProvider.js +++ b/src/components/framework/PageProvider.js @@ -24,7 +24,6 @@ const PageContext = createContext(null); */ export const usePage = () => { const context = useContext(PageContext); - console.log('usePage: called, context is:', context ? 'available' : 'null'); if (!context) { console.error('usePage: PageContext is null - component not wrapped in PageProvider'); throw new Error('usePage must be used within a PageProvider'); @@ -39,9 +38,6 @@ const determinePageType = (params) => { const { user, repo } = params; const asset = params['*']; // Wildcard parameter for asset path - console.log('PageProvider: determinePageType called with params:', params); - console.log('PageProvider: extracted values:', { user, repo, asset }); - if (asset) return PAGE_TYPES.ASSET; if (user && repo) return PAGE_TYPES.DAK; if (user) return PAGE_TYPES.USER; @@ -56,12 +52,6 @@ export const PageProvider = ({ children, pageName }) => { const location = useLocation(); const navigate = useNavigate(); - console.log('PageProvider: initialized with:', { - pageName, - params, - locationPathname: location.pathname - }); - const [pageState, setPageState] = useState({ type: determinePageType(params), pageName, diff --git a/src/services/actorDefinitionService.js b/src/services/actorDefinitionService.js index 3d9702252..283ad3858 100644 --- a/src/services/actorDefinitionService.js +++ b/src/services/actorDefinitionService.js @@ -18,7 +18,7 @@ class ActorDefinitionService { */ async loadSchema() { try { - const response = await fetch('/schemas/actor-definition.json'); + const response = await fetch(`${process.env.PUBLIC_URL || ''}/schemas/actor-definition.json`); this.actorSchema = await response.json(); } catch (error) { console.warn('Could not load actor definition schema:', error); diff --git a/src/services/githubService.js b/src/services/githubService.js index 1fc80be6f..26bcda3e7 100644 --- a/src/services/githubService.js +++ b/src/services/githubService.js @@ -998,8 +998,10 @@ class GitHubService { // Recursively fetch BPMN files from a directory and its subdirectories async getBpmnFilesRecursive(owner, repo, path, ref = 'main', allFiles = []) { try { + console.log(`🔎 githubService.getBpmnFilesRecursive: Searching ${owner}/${repo}/${path} (ref: ${ref})`); // Use authenticated octokit if available, otherwise create a public instance const octokit = this.isAuth() ? this.octokit : new Octokit(); + console.log(`🔐 githubService.getBpmnFilesRecursive: Using ${this.isAuth() ? 'authenticated' : 'public'} octokit`); const { data } = await octokit.rest.repos.getContent({ owner, @@ -1008,9 +1010,12 @@ class GitHubService { ref }); + console.log(`📦 githubService.getBpmnFilesRecursive: Received data type: ${Array.isArray(data) ? 'array' : 'single file'}, length: ${Array.isArray(data) ? data.length : 1}`); + // Handle single file response if (!Array.isArray(data)) { if (data.name.endsWith('.bpmn')) { + console.log(`📄 githubService.getBpmnFilesRecursive: Found single BPMN file: ${data.name}`); allFiles.push(data); } return allFiles; @@ -1019,15 +1024,19 @@ class GitHubService { // Handle directory response for (const item of data) { if (item.type === 'file' && item.name.endsWith('.bpmn')) { + console.log(`📄 githubService.getBpmnFilesRecursive: Found BPMN file: ${item.name}`); allFiles.push(item); } else if (item.type === 'dir') { + console.log(`📁 githubService.getBpmnFilesRecursive: Found subdirectory: ${item.name}, recursing...`); // Recursively search subdirectories await this.getBpmnFilesRecursive(owner, repo, item.path, ref, allFiles); } } + console.log(`✅ githubService.getBpmnFilesRecursive: Completed search of ${path}, found ${allFiles.length} total files so far`); return allFiles; } catch (error) { + console.log(`❌ githubService.getBpmnFilesRecursive: Error searching ${path}:`, error.status, error.message); // If directory doesn't exist, return empty array (not an error) if (error.status === 404) { return allFiles; @@ -1038,25 +1047,28 @@ class GitHubService { // Get all BPMN files from a repository's business process directories async getBpmnFiles(owner, repo, ref = 'main') { + console.log(`🔍 githubService.getBpmnFiles: Starting search for ${owner}/${repo} (ref: ${ref})`); const allBpmnFiles = []; - // Try multiple possible directory names where BPMN files might be stored + // Search for BPMN files in the specified business process directories const possiblePaths = [ 'input/business-processes', - 'input/business-process', - 'public/docs/workflows', - 'docs/workflows', - 'workflows', - 'bpmn', - 'processes' + 'input/business-process' ]; for (const path of possiblePaths) { try { + console.log(`📁 githubService.getBpmnFiles: Searching in directory: ${path}`); const files = await this.getBpmnFilesRecursive(owner, repo, path, ref); + console.log(`✅ githubService.getBpmnFiles: Found ${files.length} BPMN files in ${path}`); allBpmnFiles.push(...files); } catch (error) { - console.warn(`Could not fetch BPMN files from ${path}:`, error.message); + // Only log warnings for unexpected errors (not 404s which are expected when directories don't exist) + if (error.status !== 404) { + console.warn(`❌ Could not fetch BPMN files from ${path}:`, error.message); + } else { + console.log(`📂 githubService.getBpmnFiles: Directory ${path} not found (404) - this is expected if the directory doesn't exist`); + } // Continue trying other paths } } @@ -1066,6 +1078,8 @@ class GitHubService { index === self.findIndex(f => f.path === file.path) ); + console.log(`🎯 githubService.getBpmnFiles: Final result - ${uniqueFiles.length} unique BPMN files found`); + console.log(`📋 githubService.getBpmnFiles: File list:`, uniqueFiles.map(f => f.name)); return uniqueFiles; } diff --git a/src/tests/githubService.bpmn.test.js b/src/tests/githubService.bpmn.test.js index 81799dd3f..f744f2006 100644 --- a/src/tests/githubService.bpmn.test.js +++ b/src/tests/githubService.bpmn.test.js @@ -194,5 +194,49 @@ describe('GitHubService BPMN functionality', () => { expect(files).toHaveLength(1); expect(files[0].name).toBe('duplicate.bpmn'); }); + + it('should suppress console warnings for 404 errors', async () => { + const mockError = new Error('Not Found'); + mockError.status = 404; + + // Create a spy on console.warn to verify it's not called for 404 errors + const consoleSpy = jest.spyOn(console, 'warn').mockImplementation(() => {}); + + // Mock all paths to return 404 errors + mockOctokit.rest.repos.getContent.mockRejectedValue(mockError); + + const files = await githubService.getBpmnFiles('owner', 'repo'); + + // Should return empty array when no directories exist + expect(files).toEqual([]); + + // console.warn should not have been called for 404 errors + expect(consoleSpy).not.toHaveBeenCalled(); + + consoleSpy.mockRestore(); + }); + + it('should still log warnings for non-404 errors', async () => { + const mockError = new Error('Internal Server Error'); + mockError.status = 500; + + // Create a spy on console.warn to verify it's called for non-404 errors + const consoleSpy = jest.spyOn(console, 'warn').mockImplementation(() => {}); + + // Mock first path to return 500 error, second path to succeed with empty results + mockOctokit.rest.repos.getContent + .mockRejectedValueOnce(mockError) + .mockResolvedValue({ data: [] }); + + const files = await githubService.getBpmnFiles('owner', 'repo'); + + // Should return empty array + expect(files).toEqual([]); + + // console.warn should have been called for the 500 error + expect(consoleSpy).toHaveBeenCalledWith('Could not fetch BPMN files from input/business-processes:', 'Internal Server Error'); + + consoleSpy.mockRestore(); + }); }); }); \ No newline at end of file