Reorder deploy wizard: env vars on step 1, app storage on step 3.
Move Environment Variables to Basic Info and Application Data Storage to Resources so users configure them earlier and later in the flow. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1153,6 +1153,74 @@ export default function DeployPage() {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{/* Environment Variables */}
|
||||||
|
<div>
|
||||||
|
<label className="block text-sm font-medium text-gray-700 mb-2">Environment Variables</label>
|
||||||
|
<div className="flex flex-col sm:flex-row gap-2 mb-3">
|
||||||
|
<input
|
||||||
|
className="input-field flex-1"
|
||||||
|
placeholder="KEY"
|
||||||
|
value={envKey}
|
||||||
|
onChange={(e) => setEnvKey(e.target.value)}
|
||||||
|
/>
|
||||||
|
<input
|
||||||
|
className="input-field flex-1"
|
||||||
|
placeholder="value"
|
||||||
|
value={envVal}
|
||||||
|
onChange={(e) => setEnvVal(e.target.value)}
|
||||||
|
/>
|
||||||
|
<button type="button" onClick={addEnvVar} className="btn-secondary shrink-0">Add</button>
|
||||||
|
</div>
|
||||||
|
<div className="mb-3">
|
||||||
|
{envFile ? (
|
||||||
|
<div className="flex items-center justify-between p-3 bg-green-50 border border-green-200 rounded-xl">
|
||||||
|
<div className="flex items-center space-x-2 min-w-0">
|
||||||
|
<CheckCircle className="w-4 h-4 text-green-600 shrink-0" />
|
||||||
|
<span className="text-sm text-green-800 truncate">{envFile.name}</span>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => {
|
||||||
|
setEnvFile(null);
|
||||||
|
if (envFileInputRef.current) envFileInputRef.current.value = '';
|
||||||
|
}}
|
||||||
|
className="text-sm text-red-500 hover:text-red-700 font-medium shrink-0 ml-2"
|
||||||
|
>
|
||||||
|
Clear
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => envFileInputRef.current?.click()}
|
||||||
|
className="w-full flex items-center justify-center gap-2 p-3 border-2 border-dashed border-gray-300 rounded-xl text-sm text-gray-600 hover:border-primary-400 hover:text-primary-600 transition-colors"
|
||||||
|
>
|
||||||
|
<Upload className="w-4 h-4" />
|
||||||
|
Upload .env file
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
<input
|
||||||
|
ref={envFileInputRef}
|
||||||
|
type="file"
|
||||||
|
accept=".env,.env.local,.env.production,.env.example"
|
||||||
|
className="hidden"
|
||||||
|
onChange={(e) => {
|
||||||
|
const file = e.target.files?.[0];
|
||||||
|
if (file) handleEnvFileSelect(file);
|
||||||
|
if (envFileInputRef.current) envFileInputRef.current.value = '';
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
{Object.entries(form.envVars || {}).map(([key, value]) => (
|
||||||
|
<div key={key} className="flex items-center justify-between bg-gray-50 rounded-lg px-3 py-2 mb-2">
|
||||||
|
<span className="text-sm font-mono">
|
||||||
|
<strong>{key}</strong> = {value}
|
||||||
|
</span>
|
||||||
|
<button onClick={() => removeEnvVar(key)} className="text-red-500 text-sm">Remove</button>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
@@ -1929,68 +1997,6 @@ export default function DeployPage() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* App Storage Size (all app types) */}
|
|
||||||
<div className="bg-green-50 rounded-xl p-5 border border-green-200">
|
|
||||||
<div className="flex items-center gap-2 mb-3">
|
|
||||||
<FolderUp className="w-5 h-5 text-green-600" />
|
|
||||||
<h3 className="font-semibold text-gray-900">
|
|
||||||
{form.runtime === 'wordpress' ? 'Upload Storage (wp-content)'
|
|
||||||
: form.runtime === 'laravel' ? 'Storage Directory'
|
|
||||||
: 'Application Data Storage'}
|
|
||||||
</h3>
|
|
||||||
</div>
|
|
||||||
<p className="text-sm text-gray-600 mb-4">
|
|
||||||
{form.runtime === 'wordpress'
|
|
||||||
? 'This space is used for uploads, plugins, themes, and other WordPress files.'
|
|
||||||
: form.runtime === 'laravel'
|
|
||||||
? 'This space is used for uploads, logs, cache, and other Laravel storage files.'
|
|
||||||
: 'This space is used for persistent application data, uploads, and files.'}
|
|
||||||
</p>
|
|
||||||
<div className="flex items-center gap-3">
|
|
||||||
<div className="flex items-center border border-gray-300 rounded-lg overflow-hidden bg-white">
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
onClick={() => {
|
|
||||||
const current = parseInt(form.appStorageSize || '2', 10);
|
|
||||||
if (current > 1) setForm({ ...form, appStorageSize: String(current - 1) });
|
|
||||||
}}
|
|
||||||
disabled={parseInt(form.appStorageSize || '2', 10) <= 1}
|
|
||||||
className="px-3 py-2 bg-gray-100 hover:bg-gray-200 text-gray-700 font-bold disabled:opacity-30 disabled:cursor-not-allowed transition-colors"
|
|
||||||
>
|
|
||||||
−
|
|
||||||
</button>
|
|
||||||
<input
|
|
||||||
type="number"
|
|
||||||
min={1}
|
|
||||||
max={100}
|
|
||||||
value={form.appStorageSize || '2'}
|
|
||||||
onChange={(e) => {
|
|
||||||
const val = Math.max(1, Math.min(100, parseInt(e.target.value, 10) || 2));
|
|
||||||
setForm({ ...form, appStorageSize: String(val) });
|
|
||||||
}}
|
|
||||||
className="w-16 text-center py-2 border-x border-gray-300 text-sm font-semibold focus:outline-none"
|
|
||||||
/>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
onClick={() => {
|
|
||||||
const current = parseInt(form.appStorageSize || '2', 10);
|
|
||||||
if (current < 100) setForm({ ...form, appStorageSize: String(current + 1) });
|
|
||||||
}}
|
|
||||||
disabled={parseInt(form.appStorageSize || '2', 10) >= 100}
|
|
||||||
className="px-3 py-2 bg-gray-100 hover:bg-gray-200 text-gray-700 font-bold disabled:opacity-30 disabled:cursor-not-allowed transition-colors"
|
|
||||||
>
|
|
||||||
+
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<span className="text-sm font-medium text-gray-700">GB</span>
|
|
||||||
{wpContentFile && form.runtime === 'wordpress' && (
|
|
||||||
<span className="text-xs text-green-600">
|
|
||||||
Suggested based on wp-content ({(wpContentFile.size / (1024 * 1024 * 1024)).toFixed(2)} GB)
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
<p className="mt-2 text-xs text-gray-500">Minimum 1GB • Recommended: 2GB or more</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
@@ -2287,73 +2293,69 @@ export default function DeployPage() {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Environment Variables */}
|
{/* App Storage Size (all app types) */}
|
||||||
<div>
|
<div className="bg-green-50 rounded-xl p-5 border border-green-200">
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-2">Environment Variables</label>
|
<div className="flex items-center gap-2 mb-3">
|
||||||
<div className="flex flex-col sm:flex-row gap-2 mb-3">
|
<FolderUp className="w-5 h-5 text-green-600" />
|
||||||
<input
|
<h3 className="font-semibold text-gray-900">
|
||||||
className="input-field flex-1"
|
{form.runtime === 'wordpress' ? 'Upload Storage (wp-content)'
|
||||||
placeholder="KEY"
|
: form.runtime === 'laravel' ? 'Storage Directory'
|
||||||
value={envKey}
|
: 'Application Data Storage'}
|
||||||
onChange={(e) => setEnvKey(e.target.value)}
|
</h3>
|
||||||
/>
|
|
||||||
<input
|
|
||||||
className="input-field flex-1"
|
|
||||||
placeholder="value"
|
|
||||||
value={envVal}
|
|
||||||
onChange={(e) => setEnvVal(e.target.value)}
|
|
||||||
/>
|
|
||||||
<button type="button" onClick={addEnvVar} className="btn-secondary shrink-0">Add</button>
|
|
||||||
</div>
|
|
||||||
<div className="mb-3">
|
|
||||||
{envFile ? (
|
|
||||||
<div className="flex items-center justify-between p-3 bg-green-50 border border-green-200 rounded-xl">
|
|
||||||
<div className="flex items-center space-x-2 min-w-0">
|
|
||||||
<CheckCircle className="w-4 h-4 text-green-600 shrink-0" />
|
|
||||||
<span className="text-sm text-green-800 truncate">{envFile.name}</span>
|
|
||||||
</div>
|
</div>
|
||||||
|
<p className="text-sm text-gray-600 mb-4">
|
||||||
|
{form.runtime === 'wordpress'
|
||||||
|
? 'This space is used for uploads, plugins, themes, and other WordPress files.'
|
||||||
|
: form.runtime === 'laravel'
|
||||||
|
? 'This space is used for uploads, logs, cache, and other Laravel storage files.'
|
||||||
|
: 'This space is used for persistent application data, uploads, and files.'}
|
||||||
|
</p>
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
|
<div className="flex items-center border border-gray-300 rounded-lg overflow-hidden bg-white">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setEnvFile(null);
|
const current = parseInt(form.appStorageSize || '2', 10);
|
||||||
if (envFileInputRef.current) envFileInputRef.current.value = '';
|
if (current > 1) setForm({ ...form, appStorageSize: String(current - 1) });
|
||||||
}}
|
}}
|
||||||
className="text-sm text-red-500 hover:text-red-700 font-medium shrink-0 ml-2"
|
disabled={parseInt(form.appStorageSize || '2', 10) <= 1}
|
||||||
|
className="px-3 py-2 bg-gray-100 hover:bg-gray-200 text-gray-700 font-bold disabled:opacity-30 disabled:cursor-not-allowed transition-colors"
|
||||||
>
|
>
|
||||||
Clear
|
−
|
||||||
</button>
|
</button>
|
||||||
</div>
|
<input
|
||||||
) : (
|
type="number"
|
||||||
|
min={1}
|
||||||
|
max={100}
|
||||||
|
value={form.appStorageSize || '2'}
|
||||||
|
onChange={(e) => {
|
||||||
|
const val = Math.max(1, Math.min(100, parseInt(e.target.value, 10) || 2));
|
||||||
|
setForm({ ...form, appStorageSize: String(val) });
|
||||||
|
}}
|
||||||
|
className="w-16 text-center py-2 border-x border-gray-300 text-sm font-semibold focus:outline-none"
|
||||||
|
/>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => envFileInputRef.current?.click()}
|
onClick={() => {
|
||||||
className="w-full flex items-center justify-center gap-2 p-3 border-2 border-dashed border-gray-300 rounded-xl text-sm text-gray-600 hover:border-primary-400 hover:text-primary-600 transition-colors"
|
const current = parseInt(form.appStorageSize || '2', 10);
|
||||||
>
|
if (current < 100) setForm({ ...form, appStorageSize: String(current + 1) });
|
||||||
<Upload className="w-4 h-4" />
|
|
||||||
Upload .env file
|
|
||||||
</button>
|
|
||||||
)}
|
|
||||||
<input
|
|
||||||
ref={envFileInputRef}
|
|
||||||
type="file"
|
|
||||||
accept=".env,.env.local,.env.production,.env.example"
|
|
||||||
className="hidden"
|
|
||||||
onChange={(e) => {
|
|
||||||
const file = e.target.files?.[0];
|
|
||||||
if (file) handleEnvFileSelect(file);
|
|
||||||
if (envFileInputRef.current) envFileInputRef.current.value = '';
|
|
||||||
}}
|
}}
|
||||||
/>
|
disabled={parseInt(form.appStorageSize || '2', 10) >= 100}
|
||||||
|
className="px-3 py-2 bg-gray-100 hover:bg-gray-200 text-gray-700 font-bold disabled:opacity-30 disabled:cursor-not-allowed transition-colors"
|
||||||
|
>
|
||||||
|
+
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
{Object.entries(form.envVars || {}).map(([key, value]) => (
|
<span className="text-sm font-medium text-gray-700">GB</span>
|
||||||
<div key={key} className="flex items-center justify-between bg-gray-50 rounded-lg px-3 py-2 mb-2">
|
{wpContentFile && form.runtime === 'wordpress' && (
|
||||||
<span className="text-sm font-mono">
|
<span className="text-xs text-green-600">
|
||||||
<strong>{key}</strong> = {value}
|
Suggested based on wp-content ({(wpContentFile.size / (1024 * 1024 * 1024)).toFixed(2)} GB)
|
||||||
</span>
|
</span>
|
||||||
<button onClick={() => removeEnvVar(key)} className="text-red-500 text-sm">Remove</button>
|
)}
|
||||||
</div>
|
</div>
|
||||||
))}
|
<p className="mt-2 text-xs text-gray-500">Minimum 1GB • Recommended: 2GB or more</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user