@@ -27,6 +27,8 @@ import {
2727 RotateCcw ,
2828 Plus ,
2929 Settings ,
30+ Eye ,
31+ EyeOff ,
3032} from 'lucide-react' ;
3133import type { Team } from '@/services/types' ;
3234import {
@@ -101,6 +103,7 @@ export default function DashboardMainSection({
101103 // Environment variables state
102104 const [ envVars , setEnvVars ] = useState < Array < { key : string ; value : string } > > ( [ ] ) ;
103105 const [ isEnvVarsLoaded , setIsEnvVarsLoaded ] = useState ( false ) ;
106+ const [ showDevelopmentVars , setShowDevelopmentVars ] = useState ( false ) ;
104107
105108 // Load environment variables from localStorage on mount and when team changes
106109 useEffect ( ( ) => {
@@ -462,7 +465,7 @@ export default function DashboardMainSection({
462465 await streamingDeploy . deploy ( {
463466 githubUrl : githubUrl . trim ( ) ,
464467 dataFile : dataFile || undefined ,
465- envVars : Object . keys ( envVarsObject ) . length > 0 ? envVarsObject : undefined ,
468+ extraEnvVars : Object . keys ( envVarsObject ) . length > 0 ? envVarsObject : undefined ,
466469 } ) ;
467470
468471 setGithubUrl ( '' ) ; // Clear input on success
@@ -1373,56 +1376,171 @@ export default function DashboardMainSection({
13731376 </ div >
13741377
13751378 { /* Environment Variables Section */ }
1376- < Card className = "p-4" >
1377- < div className = "space-y-3" >
1378- < div className = "flex items-center justify-between" >
1379+ < div className = "space-y-3" >
1380+ { /* Issued Environment Variables (from backend) */ }
1381+ < Card className = "p-4" >
1382+ < div className = "space-y-3" >
13791383 < div className = "flex items-center gap-2" >
13801384 < Settings className = "h-4 w-4 text-gray-600" />
1381- < span className = "text-sm font-medium text-gray-700" > Environment Variables</ span >
1385+ < span className = "text-sm font-medium text-gray-700" >
1386+ Issued Environment Variables
1387+ </ span >
13821388 </ div >
1383- < Button
1384- variant = "outline"
1385- size = "sm"
1386- onClick = { handleAddEnvVar }
1387- className = "flex items-center gap-1"
1388- >
1389- < Plus className = "h-3 w-3" />
1390- Add
1391- </ Button >
1392- </ div >
1393-
1394- { envVars . length > 0 && (
1395- < div className = "space-y-2" >
1396- { envVars . map ( ( envVar , index ) => (
1397- < div key = { index } className = "flex gap-2 items-center" >
1398- < input
1399- type = "text"
1400- value = { envVar . key }
1401- onChange = { ( e ) => handleEnvVarKeyChange ( index , e . target . value ) }
1402- placeholder = "KEY"
1403- className = "flex-1 p-2 border border-gray-300 rounded-md text-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent font-mono"
1404- />
1405- < input
1406- type = "text"
1407- value = { envVar . value }
1408- onChange = { ( e ) => handleEnvVarValueChange ( index , e . target . value ) }
1409- placeholder = "value"
1410- className = "flex-1 p-2 border border-gray-300 rounded-md text-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent font-mono"
1411- />
1412- < button
1413- type = "button"
1414- onClick = { ( ) => handleRemoveEnvVar ( index ) }
1415- className = "text-gray-400 hover:text-red-600 p-1"
1416- aria-label = "Remove variable"
1417- >
1418- < X className = "h-4 w-4" />
1419- </ button >
1389+ { team . environments && team . environments . length > 0 ? (
1390+ < div className = "space-y-4" >
1391+ { /* Production subsection */ }
1392+ < div >
1393+ < div className = "text-xs font-medium text-gray-500 uppercase tracking-wide mb-2" >
1394+ Production
1395+ </ div >
1396+ < div className = "space-y-2" >
1397+ { team . environments
1398+ . filter ( ( e ) => e . scope === 'PRODUCTION' )
1399+ . map ( ( env ) => (
1400+ < div
1401+ key = { env . id }
1402+ className = "flex gap-2 items-center font-mono text-sm"
1403+ >
1404+ < span className = "flex-1 text-gray-700" > { env . keyName } </ span >
1405+ < span className = "flex-1 text-gray-600 truncate" >
1406+ { env . isSecret && ! canBypassLock
1407+ ? '••••••••'
1408+ : env . keyValue ?? '••••••••' }
1409+ </ span >
1410+ { env . isSecret && (
1411+ < Lock className = "h-3.5 w-3.5 text-gray-400 shrink-0" />
1412+ ) }
1413+ </ div >
1414+ ) ) }
1415+ </ div >
14201416 </ div >
1421- ) ) }
1417+ { /* Development subsection - hidden by default */ }
1418+ { team . environments . some ( ( e ) => e . scope === 'DEVELOPMENT' ) && (
1419+ < div >
1420+ < div className = "flex items-center gap-2 mb-2" >
1421+ < div className = "text-xs font-medium text-gray-500 uppercase tracking-wide" >
1422+ Development
1423+ </ div >
1424+ < Button
1425+ variant = "ghost"
1426+ size = "sm"
1427+ onClick = { ( ) =>
1428+ setShowDevelopmentVars ( ( v ) => ! v )
1429+ }
1430+ className = "h-6 px-2 text-xs text-gray-600"
1431+ >
1432+ { showDevelopmentVars ? (
1433+ < >
1434+ < EyeOff className = "h-3 w-3 mr-1" />
1435+ Hide
1436+ </ >
1437+ ) : (
1438+ < >
1439+ < Eye className = "h-3 w-3 mr-1" />
1440+ Show
1441+ </ >
1442+ ) }
1443+ </ Button >
1444+ </ div >
1445+ { showDevelopmentVars ? (
1446+ < >
1447+ < p className = "text-xs text-amber-600 mb-2" >
1448+ Development variables are not injected at runtime. They are for
1449+ local development reference only.
1450+ </ p >
1451+ < div className = "space-y-2" >
1452+ { team . environments
1453+ . filter ( ( e ) => e . scope === 'DEVELOPMENT' )
1454+ . map ( ( env ) => (
1455+ < div
1456+ key = { env . id }
1457+ className = "flex gap-2 items-center font-mono text-sm"
1458+ >
1459+ < span className = "flex-1 text-gray-700" > { env . keyName } </ span >
1460+ < span className = "flex-1 text-gray-600 truncate" >
1461+ { env . isSecret && ! canBypassLock
1462+ ? '••••••••'
1463+ : env . keyValue ?? '••••••••' }
1464+ </ span >
1465+ { env . isSecret && (
1466+ < Lock className = "h-3.5 w-3.5 text-gray-400 shrink-0" />
1467+ ) }
1468+ </ div >
1469+ ) ) }
1470+ </ div >
1471+ </ >
1472+ ) : (
1473+ < p className = "text-xs text-gray-400 italic" >
1474+ { team . environments . filter ( ( e ) => e . scope === 'DEVELOPMENT' ) . length } { ' ' }
1475+ development variable(s) hidden
1476+ </ p >
1477+ ) }
1478+ </ div >
1479+ ) }
1480+ </ div >
1481+ ) : (
1482+ < p className = "text-sm text-gray-500" > No issued environment variables.</ p >
1483+ ) }
1484+ </ div >
1485+ </ Card >
1486+
1487+ { /* Extra Environment Variables (user-added for deployment) */ }
1488+ < Card className = "p-4" >
1489+ < div className = "space-y-3" >
1490+ < div className = "flex items-center justify-between" >
1491+ < div className = "flex items-center gap-2" >
1492+ < Plus className = "h-4 w-4 text-gray-600" />
1493+ < span className = "text-sm font-medium text-gray-700" >
1494+ Extra Environment Variables
1495+ </ span >
1496+ </ div >
1497+ < Button
1498+ variant = "outline"
1499+ size = "sm"
1500+ onClick = { handleAddEnvVar }
1501+ className = "flex items-center gap-1"
1502+ >
1503+ < Plus className = "h-3 w-3" />
1504+ Add
1505+ </ Button >
14221506 </ div >
1423- ) }
1424- </ div >
1425- </ Card >
1507+ < p className = "text-xs text-gray-500" >
1508+ Add custom variables to inject during deployment (in addition to issued
1509+ variables).
1510+ </ p >
1511+ { envVars . length > 0 && (
1512+ < div className = "space-y-2" >
1513+ { envVars . map ( ( envVar , index ) => (
1514+ < div key = { index } className = "flex gap-2 items-center" >
1515+ < input
1516+ type = "text"
1517+ value = { envVar . key }
1518+ onChange = { ( e ) => handleEnvVarKeyChange ( index , e . target . value ) }
1519+ placeholder = "KEY"
1520+ className = "flex-1 p-2 border border-gray-300 rounded-md text-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent font-mono"
1521+ />
1522+ < input
1523+ type = "text"
1524+ value = { envVar . value }
1525+ onChange = { ( e ) => handleEnvVarValueChange ( index , e . target . value ) }
1526+ placeholder = "value"
1527+ className = "flex-1 p-2 border border-gray-300 rounded-md text-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent font-mono"
1528+ />
1529+ < button
1530+ type = "button"
1531+ onClick = { ( ) => handleRemoveEnvVar ( index ) }
1532+ className = "text-gray-400 hover:text-red-600 p-1"
1533+ aria-label = "Remove variable"
1534+ >
1535+ < X className = "h-4 w-4" />
1536+ </ button >
1537+ </ div >
1538+ ) ) }
1539+ </ div >
1540+ ) }
1541+ </ div >
1542+ </ Card >
1543+ </ div >
14261544
14271545 { deploymentSuccess && (
14281546 < div className = "flex items-center gap-2 text-green-600 text-sm" >
0 commit comments