Skip to content

Commit 6aeadbc

Browse files
authored
Merge pull request #32 from TsotneMikadze/develop
Develop
2 parents bc19880 + 39d5834 commit 6aeadbc

File tree

9 files changed

+342
-67
lines changed

9 files changed

+342
-67
lines changed

src/App.tsx

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { apiService } from './services/api';
88
import { exportFlowData } from './utils';
99
import { Node, Edge } from 'reactflow';
1010
import './App.css';
11+
import { SidebarTabs, MainLayout } from './components';
1112

1213
function ProtectedRoute({ children }: { children: React.ReactNode }) {
1314
const { isAuthenticated, isLoading } = useAuth();
@@ -34,10 +35,7 @@ function ProtectedRoute({ children }: { children: React.ReactNode }) {
3435
return <>{children}</>;
3536
}
3637

37-
const MainLayout = React.lazy(async () => {
38-
const mod = await import('./components/layout/MainLayout');
39-
return { default: mod.MainLayout } as { default: React.ComponentType<any> };
40-
});
38+
// Render MainLayout immediately without lazy loading for consistent UX across routes
4139

4240
function AppContent() {
4341
const { user, signOut } = useAuth();
@@ -78,16 +76,16 @@ function AppContent() {
7876
};
7977

8078
return (
81-
<React.Suspense fallback={<div>Loading...</div>}>
82-
<MainLayout
83-
onDeploy={handleDeploy}
84-
onSave={handleSave}
85-
onLoad={handleLoad}
86-
onNew={handleNew}
87-
user={user}
88-
onLogout={handleLogout}
89-
/>
90-
</React.Suspense>
79+
<MainLayout
80+
onDeploy={handleDeploy}
81+
onSave={handleSave}
82+
onLoad={handleLoad}
83+
onNew={handleNew}
84+
user={user}
85+
onLogout={handleLogout}
86+
leftSidebar={<SidebarTabs width={350} />}
87+
leftSidebarWidth={350}
88+
/>
9189
);
9290
}
9391

@@ -98,14 +96,14 @@ function App() {
9896
<Routes>
9997
<Route path="/auth" element={<AuthPage />} />
10098
<Route path="/" element={
101-
<ProtectedRoute>
99+
<ProtectedRoute>
102100
<HomePage />
103101
</ProtectedRoute>
104102
} />
105103
<Route path="/mml" element={
106104
<ProtectedRoute>
107105
<AppContent />
108-
</ProtectedRoute>
106+
</ProtectedRoute>
109107
} />
110108
<Route path="/project" element={
111109
<ProtectedRoute>

src/components/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ export { EcoreFileBox } from './flow/EcoreFileBox';
1010

1111
// UI components
1212
export { ToolsPanel } from './ui/ToolsPanel';
13+
export { SidebarTabs } from './ui/SidebarTabs';
1314
export { ConfirmDialog } from './ui/ConfirmDialog';
1415
export { CreateModelModal } from './ui/CreateModelModal';
1516
export { KeywordTagsInput } from './ui/KeywordTagsInput';

src/components/layout/Header.tsx

Lines changed: 0 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import React, { useEffect, useRef, useState } from 'react';
2-
import { Link, useLocation } from 'react-router-dom';
32
import { User } from '../../services/auth';
43

54
interface HeaderProps {
@@ -47,8 +46,6 @@ export function Header({ title = 'Vitruvius Modeler', user, onLogout }: HeaderPr
4746
};
4847
}, [isMenuOpen]);
4948

50-
const location = useLocation();
51-
5249
return (
5350
<header className="header-responsive" style={{
5451
height: 48,
@@ -70,26 +67,6 @@ export function Header({ title = 'Vitruvius Modeler', user, onLogout }: HeaderPr
7067

7168
</div>
7269
<div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
73-
<nav style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
74-
<Link to="/mml" draggable={false} onDragStart={(e) => e.preventDefault()} style={{
75-
color: location.pathname === '/mml' ? '#3498db' : '#ecf0f1',
76-
textDecoration: 'none',
77-
padding: '6px 10px',
78-
borderRadius: 6,
79-
background: location.pathname === '/mml' ? 'rgba(52,152,219,0.15)' : 'transparent',
80-
fontWeight: 600,
81-
fontSize: 13,
82-
}}>MML</Link>
83-
<Link to="/project" draggable={false} onDragStart={(e) => e.preventDefault()} style={{
84-
color: location.pathname === '/project' ? '#3498db' : '#ecf0f1',
85-
textDecoration: 'none',
86-
padding: '6px 10px',
87-
borderRadius: 6,
88-
background: location.pathname === '/project' ? 'rgba(52,152,219,0.15)' : 'transparent',
89-
fontWeight: 600,
90-
fontSize: 13,
91-
}}>Project</Link>
92-
</nav>
9370
<div ref={menuRef} style={{ position: 'relative' }}>
9471
<button
9572
onClick={() => setIsMenuOpen((open) => !open)}

src/components/ui/CreateModelModal.tsx

Lines changed: 119 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { useState, useRef } from 'react';
1+
import React, { useState, useRef, useEffect } from 'react';
22
import { apiService } from '../../services/api';
33
import { KeywordTagsInput } from './KeywordTagsInput';
44

@@ -317,9 +317,13 @@ export const CreateModelModal: React.FC<CreateModelModalProps> = ({
317317
ecore: { progress: 0, isUploading: false },
318318
genmodel: { progress: 0, isUploading: false }
319319
});
320+
const [submitProgress, setSubmitProgress] = useState({ progress: 0, isSubmitting: false });
320321

321322
const ecoreFileInputRef = useRef<HTMLInputElement>(null);
322323
const genmodelFileInputRef = useRef<HTMLInputElement>(null);
324+
const ecoreProgressIntervalRef = useRef<number | null>(null);
325+
const genmodelProgressIntervalRef = useRef<number | null>(null);
326+
const submitProgressIntervalRef = useRef<number | null>(null);
323327

324328
const canSave = uploadedFileIds.ecoreFileId > 0 && uploadedFileIds.genModelFileId > 0 && formData.name.trim();
325329

@@ -339,7 +343,10 @@ export const CreateModelModal: React.FC<CreateModelModalProps> = ({
339343
console.log('Uploading .ecore file:', file.name, 'Size:', file.size);
340344

341345
// Simulate progress updates
342-
const progressInterval = setInterval(() => {
346+
if (ecoreProgressIntervalRef.current) {
347+
clearInterval(ecoreProgressIntervalRef.current);
348+
}
349+
ecoreProgressIntervalRef.current = window.setInterval(() => {
343350
setUploadProgress(prev => ({
344351
...prev,
345352
ecore: {
@@ -352,7 +359,10 @@ export const CreateModelModal: React.FC<CreateModelModalProps> = ({
352359
const response = await apiService.uploadFile(file, 'ECORE');
353360
console.log('Upload response:', response);
354361

355-
clearInterval(progressInterval);
362+
if (ecoreProgressIntervalRef.current) {
363+
clearInterval(ecoreProgressIntervalRef.current);
364+
ecoreProgressIntervalRef.current = null;
365+
}
356366

357367
// Complete the progress
358368
setUploadProgress(prev => ({ ...prev, ecore: { progress: 100, isUploading: false } }));
@@ -379,6 +389,10 @@ export const CreateModelModal: React.FC<CreateModelModalProps> = ({
379389
console.error('Upload error:', err);
380390
setError(`${err instanceof Error ? err.message : 'Unknown error'}`);
381391
setUploadProgress(prev => ({ ...prev, ecore: { progress: 0, isUploading: false } }));
392+
if (ecoreProgressIntervalRef.current) {
393+
clearInterval(ecoreProgressIntervalRef.current);
394+
ecoreProgressIntervalRef.current = null;
395+
}
382396
}
383397

384398
event.target.value = '';
@@ -400,7 +414,10 @@ export const CreateModelModal: React.FC<CreateModelModalProps> = ({
400414
console.log('Uploading .genmodel file:', file.name, 'Size:', file.size);
401415

402416
// Simulate progress updates
403-
const progressInterval = setInterval(() => {
417+
if (genmodelProgressIntervalRef.current) {
418+
clearInterval(genmodelProgressIntervalRef.current);
419+
}
420+
genmodelProgressIntervalRef.current = window.setInterval(() => {
404421
setUploadProgress(prev => ({
405422
...prev,
406423
genmodel: {
@@ -413,7 +430,10 @@ export const CreateModelModal: React.FC<CreateModelModalProps> = ({
413430
const response = await apiService.uploadFile(file, 'GEN_MODEL');
414431
console.log('Upload response:', response);
415432

416-
clearInterval(progressInterval);
433+
if (genmodelProgressIntervalRef.current) {
434+
clearInterval(genmodelProgressIntervalRef.current);
435+
genmodelProgressIntervalRef.current = null;
436+
}
417437

418438
// Complete the progress
419439
setUploadProgress(prev => ({ ...prev, genmodel: { progress: 100, isUploading: false } }));
@@ -440,6 +460,10 @@ export const CreateModelModal: React.FC<CreateModelModalProps> = ({
440460
console.error('Upload error:', err);
441461
setError(`Error uploading ${file.name}: ${err instanceof Error ? err.message : 'Unknown error'}`);
442462
setUploadProgress(prev => ({ ...prev, genmodel: { progress: 0, isUploading: false } }));
463+
if (genmodelProgressIntervalRef.current) {
464+
clearInterval(genmodelProgressIntervalRef.current);
465+
genmodelProgressIntervalRef.current = null;
466+
}
443467
}
444468

445469
event.target.value = '';
@@ -459,6 +483,18 @@ export const CreateModelModal: React.FC<CreateModelModalProps> = ({
459483

460484
setIsLoading(true);
461485
setError('');
486+
// Start submit progress animation
487+
if (submitProgressIntervalRef.current) {
488+
clearInterval(submitProgressIntervalRef.current);
489+
submitProgressIntervalRef.current = null;
490+
}
491+
setSubmitProgress({ progress: 0, isSubmitting: true });
492+
submitProgressIntervalRef.current = window.setInterval(() => {
493+
setSubmitProgress(prev => ({
494+
progress: Math.min(prev.progress + Math.random() * 20, 90),
495+
isSubmitting: true,
496+
}));
497+
}, 200);
462498

463499
try {
464500
const requestData: CreateModelRequest = {
@@ -475,20 +511,49 @@ export const CreateModelModal: React.FC<CreateModelModalProps> = ({
475511
const response = await apiService.createMetaModel(requestData);
476512
console.log('Meta Model creation response:', response);
477513

514+
// Finish submit progress
515+
if (submitProgressIntervalRef.current) {
516+
clearInterval(submitProgressIntervalRef.current);
517+
submitProgressIntervalRef.current = null;
518+
}
519+
setSubmitProgress({ progress: 100, isSubmitting: false });
520+
478521
setSuccess('Meta Model created successfully!');
479522
setTimeout(() => {
480523
onSuccess?.(response.data);
481524
handleClose();
482525
}, 1500);
526+
// Reset progress after brief success display if modal remains open
527+
setTimeout(() => {
528+
setSubmitProgress({ progress: 0, isSubmitting: false });
529+
}, 2000);
483530
} catch (err) {
484531
console.error('Meta Model creation error:', err);
485532
setError(`Error creating meta model: ${err instanceof Error ? err.message : 'Unknown error'}`);
533+
if (submitProgressIntervalRef.current) {
534+
clearInterval(submitProgressIntervalRef.current);
535+
submitProgressIntervalRef.current = null;
536+
}
537+
setSubmitProgress({ progress: 0, isSubmitting: false });
486538
} finally {
487539
setIsLoading(false);
488540
}
489541
};
490542

491543
const handleClose = () => {
544+
if (submitProgressIntervalRef.current) {
545+
clearInterval(submitProgressIntervalRef.current);
546+
submitProgressIntervalRef.current = null;
547+
}
548+
if (ecoreProgressIntervalRef.current) {
549+
clearInterval(ecoreProgressIntervalRef.current);
550+
ecoreProgressIntervalRef.current = null;
551+
}
552+
if (genmodelProgressIntervalRef.current) {
553+
clearInterval(genmodelProgressIntervalRef.current);
554+
genmodelProgressIntervalRef.current = null;
555+
}
556+
setSubmitProgress({ progress: 0, isSubmitting: false });
492557
setFormData({
493558
name: '',
494559
description: '',
@@ -510,13 +575,49 @@ export const CreateModelModal: React.FC<CreateModelModalProps> = ({
510575
onClose();
511576
};
512577

578+
useEffect(() => {
579+
if (!isOpen) {
580+
if (submitProgressIntervalRef.current) {
581+
clearInterval(submitProgressIntervalRef.current);
582+
submitProgressIntervalRef.current = null;
583+
}
584+
if (ecoreProgressIntervalRef.current) {
585+
clearInterval(ecoreProgressIntervalRef.current);
586+
ecoreProgressIntervalRef.current = null;
587+
}
588+
if (genmodelProgressIntervalRef.current) {
589+
clearInterval(genmodelProgressIntervalRef.current);
590+
genmodelProgressIntervalRef.current = null;
591+
}
592+
setSubmitProgress({ progress: 0, isSubmitting: false });
593+
setUploadProgress({
594+
ecore: { progress: 0, isUploading: false },
595+
genmodel: { progress: 0, isUploading: false }
596+
});
597+
}
598+
return () => {
599+
if (submitProgressIntervalRef.current) {
600+
clearInterval(submitProgressIntervalRef.current);
601+
submitProgressIntervalRef.current = null;
602+
}
603+
if (ecoreProgressIntervalRef.current) {
604+
clearInterval(ecoreProgressIntervalRef.current);
605+
ecoreProgressIntervalRef.current = null;
606+
}
607+
if (genmodelProgressIntervalRef.current) {
608+
clearInterval(genmodelProgressIntervalRef.current);
609+
genmodelProgressIntervalRef.current = null;
610+
}
611+
};
612+
}, [isOpen]);
613+
513614
if (!isOpen) return null;
514615

515616
return (
516617
<div style={modalOverlayStyle} onClick={handleClose}>
517618
<div style={modalStyle} onClick={(e) => e.stopPropagation()}>
518619
<div style={modalHeaderStyle}>
519-
<h2 style={modalTitleStyle}>Upload New Meta Model</h2>
620+
<h2 style={modalTitleStyle}>Build New Meta Model</h2>
520621
<button
521622
style={closeButtonStyle}
522623
onClick={handleClose}
@@ -527,6 +628,18 @@ export const CreateModelModal: React.FC<CreateModelModalProps> = ({
527628
</button>
528629
</div>
529630

631+
{(isLoading || submitProgress.isSubmitting) && (
632+
<>
633+
<div style={{ fontSize: '13px', color: '#5a6c7d', textAlign: 'center' }}>Submitting...</div>
634+
<div style={progressBarContainerStyle}>
635+
<div style={{ ...progressBarStyle, width: `${submitProgress.progress}%` }} />
636+
</div>
637+
<div style={progressTextStyle}>
638+
{Math.round(submitProgress.progress)}%
639+
</div>
640+
</>
641+
)}
642+
530643
{error && <div style={errorMessageStyle}>{error}</div>}
531644
{success && <div style={successMessageStyle}>{success}</div>}
532645

0 commit comments

Comments
 (0)