Add environment settings to Settings tab

Add environment configuration section in SettingsTab component with:
- Current environment type display and dropdown for standalone apps
- Disabled state with badge for linked apps (environment locked)
- Warning section for linked apps with unlink button
- Settings row styling for consistent layout

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Juan DenisandClaude Opus 4.5 committed 2026-01-19 10:50:44 -05:00
1 parent 9dbcfeab5d
commit 99bc8596a1
2 files changed
+171

No files matched your search

+95
View File
@@ -1722,6 +1722,16 @@ const LogsTab = ({ app }) => {
const SettingsTab = ({ app, onUpdate }) => {
const navigate = useNavigate();
const [deleting, setDeleting] = useState(false);
const [environmentType, setEnvironmentType] = useState(app.environment_type || 'standalone');
const [savingEnvironment, setSavingEnvironment] = useState(false);
const [unlinking, setUnlinking] = useState(false);
const envLabels = {
standalone: 'Standalone',
production: 'Production',
development: 'Development',
staging: 'Staging'
};
async function handleDelete() {
if (!confirm(`Delete ${app.name}? This action cannot be undone.`)) return;
@@ -1737,10 +1747,95 @@ const SettingsTab = ({ app, onUpdate }) => {
}
}
async function handleEnvironmentChange(newType) {
if (newType === app.environment_type) return;
setSavingEnvironment(true);
try {
await api.updateAppEnvironment(app.id, newType);
setEnvironmentType(newType);
onUpdate();
} catch (err) {
console.error('Failed to update environment:', err);
setEnvironmentType(app.environment_type || 'standalone');
} finally {
setSavingEnvironment(false);
}
}
async function handleUnlink() {
if (!confirm(`Unlink ${app.name} from its linked application? Both apps will become standalone.`)) return;
setUnlinking(true);
try {
await api.unlinkApp(app.id);
onUpdate();
} catch (err) {
console.error('Failed to unlink app:', err);
} finally {
setUnlinking(false);
}
}
return (
<div>
<h3>Application Settings</h3>
{/* Environment Configuration */}
<div className="card settings-section">
<h4>Environment Configuration</h4>
<div className="settings-row">
<div className="settings-label">
<span>Environment Type</span>
<span className="settings-hint">
{app.has_linked_app
? 'This app is linked. Unlink to change environment type.'
: 'Set how this application is used in your workflow.'}
</span>
</div>
<div className="settings-control">
{app.has_linked_app ? (
<span className={`env-badge env-${app.environment_type}`}>
{envLabels[app.environment_type] || app.environment_type}
</span>
) : (
<select
value={environmentType}
onChange={(e) => handleEnvironmentChange(e.target.value)}
disabled={savingEnvironment}
className="settings-select"
>
<option value="standalone">Standalone</option>
<option value="development">Development</option>
<option value="staging">Staging</option>
<option value="production">Production</option>
</select>
)}
{savingEnvironment && <span className="settings-saving">Saving...</span>}
</div>
</div>
{app.has_linked_app && (
<div className="settings-row settings-linked-warning">
<div className="settings-label">
<span>Linked Application</span>
<span className="settings-hint">
This app is linked to another application. Unlinking will reset both apps to standalone mode.
</span>
</div>
<div className="settings-control">
<button
className="btn btn-secondary"
onClick={handleUnlink}
disabled={unlinking}
>
{unlinking ? 'Unlinking...' : 'Unlink Application'}
</button>
</div>
</div>
)}
</div>
<div className="card danger-zone">
<h4>Danger Zone</h4>
<p>Once you delete an application, there is no going back.</p>
+76
View File
@@ -813,3 +813,79 @@
color: @text-secondary;
font-size: @font-size-sm;
}
// Settings Row (for app settings tab)
.settings-row {
display: flex;
justify-content: space-between;
align-items: flex-start;
padding: @space-4;
background: @bg-hover;
border-radius: @radius-md;
gap: @space-4;
&:not(:last-child) {
margin-bottom: @space-3;
}
&.settings-linked-warning {
background: fade(@warning, 10%);
border: 1px solid fade(@warning, 20%);
}
}
.settings-label {
flex: 1;
> span:first-child {
display: block;
font-weight: @font-weight-medium;
color: @text-primary;
margin-bottom: @space-1;
}
.settings-hint {
display: block;
font-size: @font-size-xs;
color: @text-tertiary;
}
}
.settings-control {
display: flex;
align-items: center;
gap: @space-2;
flex-shrink: 0;
}
.settings-select {
min-width: 160px;
padding: @space-2 @space-3;
background: @bg-card;
border: 1px solid @border-subtle;
border-radius: @radius-md;
color: @text-primary;
font-size: @font-size-sm;
cursor: pointer;
transition: border-color @transition-fast;
&:hover {
border-color: @border-default;
}
&:focus {
outline: none;
border-color: @accent-primary;
}
&:disabled {
opacity: 0.6;
cursor: not-allowed;
}
}
.settings-saving {
font-size: @font-size-xs;
color: @text-tertiary;
font-style: italic;
}