|
1 | 1 | import { useState } from 'react'; |
2 | 2 | import { useNavigate } from 'react-router-dom'; |
3 | | -import { Copy, ExternalLink, CheckCircle, ArrowLeft, Github, Code, Rocket, FileJson, Server, Smartphone, LayoutGrid, CopyPlus, Loader2 } from 'lucide-react'; |
| 3 | +import { Copy, ExternalLink, CheckCircle, ArrowLeft, Github, Code, Rocket, FileJson, Server, Smartphone, LayoutGrid, CopyPlus, Loader2, AlertTriangle } from 'lucide-react'; |
4 | 4 | import toast from 'react-hot-toast'; |
5 | 5 | import api from '../utils/api'; |
6 | 6 |
|
@@ -291,6 +291,7 @@ function Templates() { |
291 | 291 | const [projectName, setProjectName] = useState(''); |
292 | 292 | const [projectDescription, setProjectDescription] = useState(''); |
293 | 293 | const [isCloning, setIsCloning] = useState(false); |
| 294 | + const [clonedProject, setClonedProject] = useState(null); |
294 | 295 |
|
295 | 296 | const sdks = ['all', ...new Set(TEMPLATES.map(t => t.sdk))]; |
296 | 297 |
|
@@ -325,13 +326,13 @@ function Templates() { |
325 | 326 | }); |
326 | 327 | toast.success('Project cloned successfully!'); |
327 | 328 |
|
328 | | - const newProjectId = res.data.data?._id || res.data._id; |
| 329 | + const newProjectData = res.data.data || res.data; |
329 | 330 |
|
330 | 331 | setCloneTemplate(null); |
331 | 332 | setProjectName(''); |
332 | 333 | setProjectDescription(''); |
333 | 334 |
|
334 | | - navigate(`/project/${newProjectId}`); |
| 335 | + setClonedProject(newProjectData); |
335 | 336 | } catch (error) { |
336 | 337 | toast.error(error.response?.data?.error || 'Failed to clone project'); |
337 | 338 | } finally { |
@@ -626,6 +627,80 @@ function Templates() { |
626 | 627 | </div> |
627 | 628 | </div> |
628 | 629 | )} |
| 630 | + |
| 631 | + {/* Success Modal */} |
| 632 | + {clonedProject && ( |
| 633 | + <div style={{ |
| 634 | + position: 'fixed', top: 0, left: 0, right: 0, bottom: 0, |
| 635 | + background: 'rgba(0,0,0,0.7)', display: 'flex', alignItems: 'center', justifyContent: 'center', zIndex: 9999, overflowY: 'auto', padding: '2rem' |
| 636 | + }}> |
| 637 | + <div style={{ |
| 638 | + background: 'var(--color-bg-card)', padding: '2.5rem', borderRadius: '16px', width: '100%', maxWidth: '550px', border: '1px solid var(--color-border)', |
| 639 | + boxShadow: '0 25px 50px -12px rgba(0, 0, 0, 0.25)' |
| 640 | + }}> |
| 641 | + <div style={{ textAlign: 'center', marginBottom: '2rem' }}> |
| 642 | + <div style={{ |
| 643 | + width: '72px', height: '72px', borderRadius: '50%', background: 'rgba(16, 185, 129, 0.1)', |
| 644 | + display: 'flex', alignItems: 'center', justifyContent: 'center', margin: '0 auto 1.25rem auto' |
| 645 | + }}> |
| 646 | + <CheckCircle size={36} color="var(--color-success)" /> |
| 647 | + </div> |
| 648 | + <h2 style={{ fontSize: '1.75rem', fontWeight: 700, margin: '0 0 0.5rem', color: 'var(--color-text-main)' }}>Project Cloned!</h2> |
| 649 | + <p style={{ color: 'var(--color-text-muted)', margin: 0, fontSize: '0.95rem' }}><strong style={{ color: 'var(--color-text-main)' }}>{clonedProject.name}</strong> has been successfully initialized.</p> |
| 650 | + </div> |
| 651 | + |
| 652 | + {clonedProject.apiKeysLocked ? ( |
| 653 | + <div style={{ backgroundColor: 'rgba(62, 207, 142, 0.08)', border: '1px solid rgba(62, 207, 142, 0.25)', borderRadius: '8px', padding: '1.25rem', marginBottom: '2rem', display: 'flex', gap: '15px' }}> |
| 654 | + <AlertTriangle color="var(--color-primary)" size={24} style={{ flexShrink: 0, marginTop: '2px' }} /> |
| 655 | + <div> |
| 656 | + <strong style={{ color: 'var(--color-primary)', display: 'block', marginBottom: '4px' }}>Your backend is ready</strong> |
| 657 | + <p style={{ color: 'var(--color-text-muted)', fontSize: '0.9rem', lineHeight: '1.5', margin: 0 }}> |
| 658 | + API keys unlock after email verification. |
| 659 | + </p> |
| 660 | + </div> |
| 661 | + </div> |
| 662 | + ) : ( |
| 663 | + <> |
| 664 | + <div style={{ backgroundColor: 'rgba(239, 68, 68, 0.1)', border: '1px solid rgba(239, 68, 68, 0.2)', borderRadius: '8px', padding: '1.25rem', marginBottom: '2rem', display: 'flex', gap: '15px' }}> |
| 665 | + <AlertTriangle color="#ef4444" size={24} style={{ flexShrink: 0, marginTop: '2px' }} /> |
| 666 | + <div> |
| 667 | + <strong style={{ color: '#ef4444', display: 'block', marginBottom: '4px', fontSize: '0.95rem' }}>Save these API Keys immediately</strong> |
| 668 | + <p style={{ color: 'var(--color-text-muted)', fontSize: '0.9rem', lineHeight: '1.5', margin: 0 }}> |
| 669 | + For security reasons, these keys will <strong>only be shown once</strong>. If you lose them, you will need to regenerate them. |
| 670 | + </p> |
| 671 | + </div> |
| 672 | + </div> |
| 673 | + |
| 674 | + <div style={{ marginBottom: '1.25rem' }}> |
| 675 | + <label style={{ display: 'block', color: 'var(--color-primary)', fontSize: '0.85rem', fontWeight: 600, marginBottom: '0.5rem' }}>Publishable API Key (Frontend safe)</label> |
| 676 | + <div style={{ display: 'flex', gap: '10px' }}> |
| 677 | + <div style={{ fontFamily: 'monospace', backgroundColor: 'var(--color-bg-input)', color: 'var(--color-primary)', overflowX: 'auto', whiteSpace: 'nowrap', flex: 1, padding: '12px 14px', borderRadius: '8px', border: '1px solid var(--color-border)', fontSize: '0.9rem' }}> |
| 678 | + {clonedProject.publishableKey} |
| 679 | + </div> |
| 680 | + <button onClick={() => { navigator.clipboard.writeText(clonedProject.publishableKey); toast.success("Copied to clipboard!"); }} className="btn btn-secondary" style={{ padding: '0 14px' }}><Copy size={16} /></button> |
| 681 | + </div> |
| 682 | + </div> |
| 683 | + |
| 684 | + <div style={{ marginBottom: '2rem' }}> |
| 685 | + <label style={{ display: 'block', color: 'var(--color-error)', fontSize: '0.85rem', fontWeight: 600, marginBottom: '0.5rem' }}>Secret API Key (Backend only)</label> |
| 686 | + <div style={{ display: 'flex', gap: '10px' }}> |
| 687 | + <div style={{ fontFamily: 'monospace', backgroundColor: 'var(--color-bg-input)', color: 'var(--color-error)', overflowX: 'auto', whiteSpace: 'nowrap', flex: 1, padding: '12px 14px', borderRadius: '8px', border: '1px solid var(--color-border)', fontSize: '0.9rem' }}> |
| 688 | + {clonedProject.secretKey} |
| 689 | + </div> |
| 690 | + <button onClick={() => { navigator.clipboard.writeText(clonedProject.secretKey); toast.success("Copied to clipboard!"); }} className="btn btn-secondary" style={{ padding: '0 14px' }}><Copy size={16} /></button> |
| 691 | + </div> |
| 692 | + </div> |
| 693 | + </> |
| 694 | + )} |
| 695 | + |
| 696 | + <div style={{ display: 'flex', gap: '1rem', flexDirection: 'column' }}> |
| 697 | + <button onClick={() => navigate(`/project/${clonedProject._id}`)} className="btn btn-primary" style={{ width: '100%', padding: '12px', fontSize: '1rem', fontWeight: 600, justifyContent: 'center' }}> |
| 698 | + Go to Project Dashboard |
| 699 | + </button> |
| 700 | + </div> |
| 701 | + </div> |
| 702 | + </div> |
| 703 | + )} |
629 | 704 | </div> |
630 | 705 | ); |
631 | 706 | } |
|
0 commit comments