refactor(ui): replace settings page with deprecation notice

- Remove complex settings editor (217 lines)
- Add simple deprecation redirect to API Profiles page
- Improve UX by consolidating settings functionality
This commit is contained in:
kaitranntt
2025-12-07 20:57:30 -05:00
parent 9b4a5d80c5
commit 1f215d8ecb
+37 -216
View File
@@ -1,225 +1,46 @@
import { useState, useMemo } from 'react';
import { useSearchParams } from 'react-router-dom';
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import { Button } from '@/components/ui/button';
/**
* Settings Page - Deprecated
* Settings functionality has been moved to API Profiles page
*/
import { Link } from 'react-router-dom';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
import { MaskedInput } from '@/components/ui/masked-input';
import { ConfirmDialog } from '@/components/confirm-dialog';
import { Edit, Save, X } from 'lucide-react';
import { toast } from 'sonner';
import { api } from '@/lib/api-client';
interface Settings {
env?: Record<string, string>;
}
interface SettingsResponse {
profile: string;
settings: Settings;
mtime: number;
path: string;
}
import { Button } from '@/components/ui/button';
import { Construction, ArrowRight } from 'lucide-react';
export function SettingsPage() {
const [searchParams, setSearchParams] = useSearchParams();
const profile = searchParams.get('profile');
const [editMode, setEditMode] = useState(false);
const [editedSettings, setEditedSettings] = useState<Settings | null>(null);
const [conflictDialog, setConflictDialog] = useState(false);
const queryClient = useQueryClient();
// Fetch profiles for selector
const { data: profilesData } = useQuery({
queryKey: ['profiles'],
queryFn: () => api.profiles.list(),
});
// Fetch settings for selected profile
const { data, isLoading, refetch } = useQuery<SettingsResponse>({
queryKey: ['settings', profile],
queryFn: () => fetch(`/api/settings/${profile}/raw`).then((r) => r.json()),
enabled: !!profile,
});
// Use edited settings when in edit mode, otherwise use data directly
const currentSettings = useMemo(() => {
return editMode && editedSettings ? editedSettings : data?.settings;
}, [editMode, editedSettings, data?.settings]);
// Save mutation
const saveMutation = useMutation({
mutationFn: async () => {
const res = await fetch(`/api/settings/${profile}`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
settings: editedSettings,
expectedMtime: data?.mtime,
}),
});
if (res.status === 409) {
throw new Error('CONFLICT');
}
if (!res.ok) {
throw new Error('Failed to save');
}
return res.json();
},
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['settings', profile] });
setEditedSettings(null);
setEditMode(false);
toast.success('Settings saved');
},
onError: (error: Error) => {
if (error.message === 'CONFLICT') {
setConflictDialog(true);
} else {
toast.error(error.message);
}
},
});
const handleSave = () => {
saveMutation.mutate();
};
const handleConflictResolve = async (overwrite: boolean) => {
setConflictDialog(false);
if (overwrite) {
// Refetch to get new mtime, then save
await refetch();
saveMutation.mutate();
} else {
// Discard local changes
setEditedSettings(null);
setEditMode(false);
}
};
const updateEnvValue = (key: string, value: string) => {
setEditedSettings((prev) => ({
...prev,
env: {
...prev?.env,
[key]: value,
},
}));
};
return (
<div className="p-6 space-y-6">
<div className="flex items-center justify-between">
<h1 className="text-2xl font-bold">Settings Editor</h1>
<select
className="border rounded px-3 py-2"
value={profile || ''}
onChange={(e: React.ChangeEvent<HTMLSelectElement>) =>
setSearchParams({ profile: e.target.value })
}
>
<option value="">Select profile...</option>
{profilesData?.profiles.map((p) => (
<option key={p.name} value={p.name}>
{p.name}
</option>
))}
</select>
</div>
<h1 className="text-2xl font-bold">Settings</h1>
{!profile && (
<Card>
<CardContent className="pt-6">
<p className="text-muted-foreground">Select a profile to view/edit settings.</p>
</CardContent>
</Card>
)}
{profile && isLoading && (
<Card>
<CardContent className="pt-6">
<p className="text-muted-foreground">Loading...</p>
</CardContent>
</Card>
)}
{profile && data && currentSettings && (
<Card>
<CardHeader className="flex flex-row items-center justify-between">
<CardTitle>Environment Variables</CardTitle>
<div className="flex gap-2">
{!editMode ? (
<Button
variant="outline"
onClick={() => {
setEditedSettings(data.settings);
setEditMode(true);
}}
>
<Edit className="w-4 h-4 mr-2" /> Edit
</Button>
) : (
<>
<Button
variant="outline"
onClick={() => {
setEditedSettings(null);
setEditMode(false);
}}
>
<X className="w-4 h-4 mr-2" /> Cancel
</Button>
<Button onClick={handleSave} disabled={saveMutation.isPending}>
<Save className="w-4 h-4 mr-2" />
{saveMutation.isPending ? 'Saving...' : 'Save'}
</Button>
</>
)}
</div>
</CardHeader>
<CardContent className="space-y-4">
{Object.entries(currentSettings.env || {}).map(([key, value]) => (
<div key={key}>
<Label>{key}</Label>
{key.includes('TOKEN') || key.includes('KEY') ? (
<MaskedInput
value={value}
onChange={(e) => updateEnvValue(key, e.target.value)}
disabled={!editMode}
/>
) : (
<Input
value={value}
onChange={(e) => updateEnvValue(key, e.target.value)}
disabled={!editMode}
className="font-mono"
/>
)}
</div>
))}
<div className="pt-4 text-xs text-muted-foreground">
<p>Path: {data.path}</p>
<p>Last modified: {new Date(data.mtime).toLocaleString()}</p>
</div>
</CardContent>
</Card>
)}
<ConfirmDialog
open={conflictDialog}
title="File Modified Externally"
description="This settings file was modified by another process. Overwrite with your changes or discard?"
confirmText="Overwrite"
variant="destructive"
onConfirm={() => handleConflictResolve(true)}
onCancel={() => handleConflictResolve(false)}
/>
<Card className="border-yellow-500/50 bg-yellow-500/5">
<CardHeader>
<CardTitle className="flex items-center gap-2 text-yellow-600">
<Construction className="w-5 h-5" />
Page Relocated
</CardTitle>
</CardHeader>
<CardContent className="space-y-4">
<p className="text-muted-foreground">
The settings editor has been integrated into the <strong>API Profiles</strong> page for
a better user experience.
</p>
<p className="text-muted-foreground">To edit environment variables for a profile:</p>
<ol className="list-decimal list-inside text-muted-foreground space-y-1 ml-2">
<li>Go to API Profiles page</li>
<li>Click the actions menu (...) on any profile</li>
<li>Select &quot;Edit Settings&quot;</li>
</ol>
<div className="pt-2">
<Button asChild>
<Link to="/api">
Go to API Profiles
<ArrowRight className="w-4 h-4 ml-2" />
</Link>
</Button>
</div>
</CardContent>
</Card>
</div>
);
}