Skip to content

Commit c2ad418

Browse files
committed
fix(web-dashboard): show api keys on template clone
- Updated the template clone flow in Templates.jsx to render a Success Modal displaying the newly generated publishable and secret keys. - Previously, it immediately navigated to the project dashboard, preventing users from seeing their API keys since they are only shown once.
1 parent a08b451 commit c2ad418

1 file changed

Lines changed: 78 additions & 3 deletions

File tree

apps/web-dashboard/src/pages/Templates.jsx

Lines changed: 78 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { useState } from 'react';
22
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';
44
import toast from 'react-hot-toast';
55
import api from '../utils/api';
66

@@ -291,6 +291,7 @@ function Templates() {
291291
const [projectName, setProjectName] = useState('');
292292
const [projectDescription, setProjectDescription] = useState('');
293293
const [isCloning, setIsCloning] = useState(false);
294+
const [clonedProject, setClonedProject] = useState(null);
294295

295296
const sdks = ['all', ...new Set(TEMPLATES.map(t => t.sdk))];
296297

@@ -325,13 +326,13 @@ function Templates() {
325326
});
326327
toast.success('Project cloned successfully!');
327328

328-
const newProjectId = res.data.data?._id || res.data._id;
329+
const newProjectData = res.data.data || res.data;
329330

330331
setCloneTemplate(null);
331332
setProjectName('');
332333
setProjectDescription('');
333334

334-
navigate(`/project/${newProjectId}`);
335+
setClonedProject(newProjectData);
335336
} catch (error) {
336337
toast.error(error.response?.data?.error || 'Failed to clone project');
337338
} finally {
@@ -626,6 +627,80 @@ function Templates() {
626627
</div>
627628
</div>
628629
)}
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+
)}
629704
</div>
630705
);
631706
}

0 commit comments

Comments
 (0)