Skip to content

Commit ed321fb

Browse files
authored
Merge pull request #50 from TsotneMikadze/develop
Develop
2 parents 3a95446 + 194fdde commit ed321fb

File tree

10 files changed

+515
-103
lines changed

10 files changed

+515
-103
lines changed

src/components/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ export { SidebarTabs } from './ui/SidebarTabs';
1414
export { ConfirmDialog } from './ui/ConfirmDialog';
1515
export { CreateModelModal } from './ui/CreateModelModal';
1616
export { KeywordTagsInput } from './ui/KeywordTagsInput';
17+
export { ToastProvider, useToast } from './ui/ToastProvider';
1718

1819

1920
// Auth components

src/components/ui/CreateModelModal.tsx

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -628,17 +628,7 @@ export const CreateModelModal: React.FC<CreateModelModalProps> = ({
628628
</button>
629629
</div>
630630

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-
)}
631+
{/* Removed top-level submitting progress to show near action buttons */}
642632

643633
{error && <div style={errorMessageStyle}>{error}</div>}
644634
{success && <div style={successMessageStyle}>{success}</div>}
@@ -805,6 +795,18 @@ export const CreateModelModal: React.FC<CreateModelModalProps> = ({
805795
{isLoading ? 'Creating...' : canSave ? 'Build Meta Model' : 'Upload Files First'}
806796
</button>
807797
</div>
798+
799+
{(isLoading || submitProgress.isSubmitting) && (
800+
<>
801+
<div style={{ fontSize: '13px', color: '#5a6c7d', textAlign: 'center', marginTop: '8px' }}>Building...</div>
802+
<div style={progressBarContainerStyle}>
803+
<div style={{ ...progressBarStyle, width: `${submitProgress.progress}%` }} />
804+
</div>
805+
<div style={progressTextStyle}>
806+
{Math.round(submitProgress.progress)}%
807+
</div>
808+
</>
809+
)}
808810
</div>
809811
</div>
810812
);
Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
1+
import React, { useState } from 'react';
2+
import { apiService } from '../../services/api';
3+
import { useToast } from './ToastProvider';
4+
5+
interface CreateVsumModalProps {
6+
isOpen: boolean;
7+
onClose: () => void;
8+
onSuccess?: (vsum: any) => void;
9+
}
10+
11+
const modalOverlayStyle: React.CSSProperties = {
12+
position: 'fixed',
13+
top: 0,
14+
left: 0,
15+
right: 0,
16+
bottom: 0,
17+
background: 'rgba(0, 0, 0, 0.5)',
18+
display: 'flex',
19+
alignItems: 'center',
20+
justifyContent: 'center',
21+
zIndex: 1000,
22+
};
23+
24+
const modalStyle: React.CSSProperties = {
25+
background: '#ffffff',
26+
borderRadius: '8px',
27+
padding: '24px',
28+
width: '480px',
29+
maxWidth: '90vw',
30+
maxHeight: '85vh',
31+
overflow: 'auto',
32+
boxShadow: '0 20px 40px rgba(0, 0, 0, 0.2)',
33+
border: '1px solid #d1ecf1',
34+
fontFamily: 'Georgia, serif',
35+
};
36+
37+
const headerStyle: React.CSSProperties = {
38+
display: 'flex',
39+
justifyContent: 'space-between',
40+
alignItems: 'center',
41+
marginBottom: '16px',
42+
paddingBottom: '12px',
43+
borderBottom: '2px solid #3498db',
44+
};
45+
46+
const titleStyle: React.CSSProperties = {
47+
fontSize: '20px',
48+
fontWeight: 700,
49+
color: '#2c3e50',
50+
margin: 0,
51+
fontFamily: 'Georgia, serif',
52+
};
53+
54+
const closeButtonStyle: React.CSSProperties = {
55+
background: 'transparent',
56+
border: '1px solid #dee2e6',
57+
fontSize: '16px',
58+
color: '#6c757d',
59+
cursor: 'pointer',
60+
padding: '6px 10px',
61+
borderRadius: '6px',
62+
};
63+
64+
const formGroupStyle: React.CSSProperties = {
65+
marginBottom: '14px',
66+
};
67+
68+
const labelStyle: React.CSSProperties = {
69+
display: 'block',
70+
fontSize: '13px',
71+
fontWeight: 600,
72+
color: '#2c3e50',
73+
marginBottom: '6px',
74+
fontFamily: 'Georgia, serif',
75+
};
76+
77+
const inputStyle: React.CSSProperties = {
78+
width: '100%',
79+
padding: '10px 12px',
80+
border: '1px solid #ced4da',
81+
borderRadius: '6px',
82+
fontSize: '13px',
83+
boxSizing: 'border-box',
84+
background: '#ffffff',
85+
fontFamily: 'Georgia, serif',
86+
};
87+
88+
const buttonRowStyle: React.CSSProperties = {
89+
display: 'flex',
90+
gap: '8px',
91+
justifyContent: 'flex-end',
92+
marginTop: '12px',
93+
};
94+
95+
const primaryButtonStyle: React.CSSProperties = {
96+
padding: '10px 12px',
97+
border: '1px solid #dee2e6',
98+
borderRadius: '6px',
99+
background: '#e7f5ff',
100+
cursor: 'pointer',
101+
fontWeight: 700,
102+
};
103+
104+
const secondaryButtonStyle: React.CSSProperties = {
105+
padding: '10px 12px',
106+
border: '1px solid #dee2e6',
107+
borderRadius: '6px',
108+
background: '#f8f9fa',
109+
cursor: 'pointer',
110+
fontWeight: 600,
111+
};
112+
113+
const errorMessageStyle: React.CSSProperties = {
114+
padding: '8px 12px',
115+
margin: '8px 0',
116+
borderRadius: '6px',
117+
fontSize: '12px',
118+
fontWeight: '500',
119+
backgroundColor: '#f8d7da',
120+
color: '#721c24',
121+
border: '1px solid #f5c6cb',
122+
};
123+
124+
export const CreateVsumModal: React.FC<CreateVsumModalProps> = ({ isOpen, onClose, onSuccess }) => {
125+
const [name, setName] = useState('');
126+
const [description, setDescription] = useState('');
127+
const [loading, setLoading] = useState(false);
128+
const [error, setError] = useState('');
129+
const { showSuccess, showError } = useToast();
130+
131+
if (!isOpen) return null;
132+
133+
const handleSubmit = async () => {
134+
const trimmedName = name.trim();
135+
if (!trimmedName) {
136+
setError('Name is required');
137+
return;
138+
}
139+
setLoading(true);
140+
setError('');
141+
try {
142+
const res = await apiService.createVsum({ name: trimmedName, description: description.trim() || undefined });
143+
onSuccess?.(res.data);
144+
showSuccess('Vsum successfully created');
145+
setName('');
146+
setDescription('');
147+
onClose();
148+
} catch (e) {
149+
const msg = e instanceof Error ? e.message : 'Failed to create vSUM';
150+
setError(msg);
151+
showError(msg);
152+
} finally {
153+
setLoading(false);
154+
}
155+
};
156+
157+
const handleClose = () => {
158+
if (loading) return;
159+
setError('');
160+
setName('');
161+
setDescription('');
162+
onClose();
163+
};
164+
165+
return (
166+
<div style={modalOverlayStyle} onClick={handleClose}>
167+
<div style={modalStyle} onClick={(e) => e.stopPropagation()}>
168+
<div style={headerStyle}>
169+
<h3 style={titleStyle}>Create vSUM</h3>
170+
<button style={closeButtonStyle} onClick={handleClose}>
171+
×
172+
</button>
173+
</div>
174+
175+
{error && <div style={errorMessageStyle}>{error}</div>}
176+
177+
<div style={formGroupStyle}>
178+
<label style={labelStyle}>Name *</label>
179+
<input
180+
placeholder="Enter name"
181+
value={name}
182+
onChange={(e) => setName(e.target.value)}
183+
style={inputStyle}
184+
disabled={loading}
185+
/>
186+
</div>
187+
188+
<div style={formGroupStyle}>
189+
<label style={labelStyle}>Description</label>
190+
<textarea
191+
placeholder="Optional description"
192+
value={description}
193+
onChange={(e) => setDescription(e.target.value)}
194+
style={{ ...inputStyle, minHeight: 80, resize: 'vertical' }}
195+
disabled={loading}
196+
/>
197+
</div>
198+
199+
<div style={buttonRowStyle}>
200+
<button style={secondaryButtonStyle} onClick={handleClose} disabled={loading}>Cancel</button>
201+
<button style={primaryButtonStyle} onClick={handleSubmit} disabled={loading || !name.trim()}>
202+
{loading ? 'Creating...' : 'Create vSUM'}
203+
</button>
204+
</div>
205+
</div>
206+
</div>
207+
);
208+
};
209+

src/components/ui/MetaModelsPanel.tsx

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import React, { useEffect, useState } from 'react';
22
import { apiService } from '../../services/api';
3+
import { ConfirmDialog } from './ConfirmDialog';
34

45
interface MetaModelsPanelProps {
56
activeVsumId?: number | null;
@@ -157,6 +158,9 @@ export const MetaModelsPanel: React.FC<MetaModelsPanelProps> = ({ activeVsumId,
157158
const [showFilters, setShowFilters] = useState(false);
158159
const [dateFilter, setDateFilter] = useState<'all' | 'today' | 'week' | 'month' | 'year'>('all');
159160
const [parsedFilters, setParsedFilters] = useState<any[]>([]);
161+
const [confirmOpen, setConfirmOpen] = useState(false);
162+
const [deletingId, setDeletingId] = useState<number | null>(null);
163+
const [isDeleting, setIsDeleting] = useState(false);
160164

161165
const handleSearchKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
162166
if (e.key !== 'Tab') return;
@@ -507,6 +511,13 @@ export const MetaModelsPanel: React.FC<MetaModelsPanelProps> = ({ activeVsumId,
507511
<span>Domain: <strong>{model.domain}</strong></span>
508512
<span></span>
509513
<span title={new Date(model.createdAt).toLocaleString()}>{formatRelativeTime(model.createdAt)}</span>
514+
<span></span>
515+
<button
516+
onClick={() => { setDeletingId(model.id); setConfirmOpen(true); }}
517+
style={{ padding: '4px 8px', border: '1px solid #dee2e6', borderRadius: 6, background: '#fff', cursor: 'pointer', fontSize: 12, fontWeight: 600, color: '#e03131' }}
518+
>
519+
Delete
520+
</button>
510521
{activeVsumId ? (
511522
<>
512523
<span></span>
@@ -577,6 +588,29 @@ export const MetaModelsPanel: React.FC<MetaModelsPanelProps> = ({ activeVsumId,
577588
</div>
578589
</div>
579590
)}
591+
<ConfirmDialog
592+
isOpen={confirmOpen}
593+
title="Delete meta model?"
594+
message="This action cannot be undone."
595+
confirmText={isDeleting ? 'Deleting…' : 'Delete'}
596+
cancelText="Cancel"
597+
onConfirm={async () => {
598+
if (deletingId == null) return;
599+
setIsDeleting(true);
600+
setApiError('');
601+
try {
602+
await apiService.deleteMetaModel(String(deletingId));
603+
setApiModels(prev => prev.filter(m => m.id !== deletingId));
604+
} catch (err) {
605+
setApiError(err instanceof Error ? err.message : 'Failed to delete meta model');
606+
} finally {
607+
setIsDeleting(false);
608+
setConfirmOpen(false);
609+
setDeletingId(null);
610+
}
611+
}}
612+
onCancel={() => { if (!isDeleting) { setConfirmOpen(false); setDeletingId(null); } }}
613+
/>
580614
</div>
581615
);
582616
};

0 commit comments

Comments
 (0)