chore(app): remove orphaned ConfigEditor page
Imported nowhere and absent from the router — an unreachable bulk-editor
superseded by the typed detail pages (ServiceDetail/ProgramDetail/
ScheduledDetail), which edit via ConfigPanel. It also called endpoints that
no longer exist (GET /components, PUT/DELETE /config/components/{name}; the
API uses /deployments and typed /config/{programs,services,jobs}/{name}).
App builds clean.
This commit is contained in:
@@ -1,146 +0,0 @@
|
||||
import { useState } from "react"
|
||||
import { Link } from "react-router-dom"
|
||||
import { ArrowLeft, Check, Loader2, Zap } from "lucide-react"
|
||||
import { useQuery, useQueryClient } from "@tanstack/react-query"
|
||||
import { apiClient } from "@/services/api/client"
|
||||
import type { DeploymentDetail } from "@/types"
|
||||
import { AddDeployment } from "@/components/AddDeployment"
|
||||
import { DeploymentEditor } from "@/components/DeploymentEditor"
|
||||
|
||||
interface ApplyResult {
|
||||
ok: boolean
|
||||
actions: string[]
|
||||
errors: string[]
|
||||
}
|
||||
|
||||
export function ConfigEditorPage() {
|
||||
const qc = useQueryClient()
|
||||
const [applying, setApplying] = useState(false)
|
||||
const [message, setMessage] = useState<{ type: "ok" | "error"; text: string } | null>(null)
|
||||
const [applyResult, setApplyResult] = useState<ApplyResult | null>(null)
|
||||
|
||||
const { data: components, isLoading, refetch } = useQuery({
|
||||
queryKey: ["config-components"],
|
||||
queryFn: async () => {
|
||||
const list = await apiClient.get<{ id: string }[]>("/components")
|
||||
const details = await Promise.all(
|
||||
list.map((c) => apiClient.get<DeploymentDetail>(`/components/${c.id}`))
|
||||
)
|
||||
return details
|
||||
},
|
||||
})
|
||||
|
||||
const handleSave = async (name: string, config: Record<string, unknown>) => {
|
||||
setMessage(null)
|
||||
setApplyResult(null)
|
||||
try {
|
||||
await apiClient.put(`/config/components/${name}`, { config })
|
||||
setMessage({ type: "ok", text: `Saved ${name}` })
|
||||
refetch()
|
||||
qc.invalidateQueries({ queryKey: ["components"] })
|
||||
} catch (e: unknown) {
|
||||
const msg = e instanceof Error ? e.message : String(e)
|
||||
setMessage({ type: "error", text: msg })
|
||||
}
|
||||
}
|
||||
|
||||
const handleDelete = async (name: string) => {
|
||||
setMessage(null)
|
||||
try {
|
||||
await apiClient.delete(`/config/components/${name}`)
|
||||
setMessage({ type: "ok", text: `Removed ${name}` })
|
||||
refetch()
|
||||
qc.invalidateQueries({ queryKey: ["components"] })
|
||||
} catch (e: unknown) {
|
||||
const msg = e instanceof Error ? e.message : String(e)
|
||||
setMessage({ type: "error", text: msg })
|
||||
}
|
||||
}
|
||||
|
||||
const handleApply = async () => {
|
||||
setApplying(true)
|
||||
setMessage(null)
|
||||
setApplyResult(null)
|
||||
try {
|
||||
const result = await apiClient.post<ApplyResult>("/config/apply")
|
||||
setApplyResult(result)
|
||||
setMessage({
|
||||
type: result.ok ? "ok" : "error",
|
||||
text: result.ok ? "Applied successfully" : "Applied with errors",
|
||||
})
|
||||
qc.invalidateQueries({ queryKey: ["components"] })
|
||||
qc.invalidateQueries({ queryKey: ["status"] })
|
||||
} catch (e: unknown) {
|
||||
const msg = e instanceof Error ? e.message : String(e)
|
||||
setMessage({ type: "error", text: msg })
|
||||
} finally {
|
||||
setApplying(false)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="max-w-4xl mx-auto px-6 py-8">
|
||||
<div className="flex items-center justify-between mb-6">
|
||||
<div className="flex items-center gap-4">
|
||||
<Link to="/" className="text-[var(--primary)] hover:underline flex items-center gap-1">
|
||||
<ArrowLeft size={16} /> Dashboard
|
||||
</Link>
|
||||
<h1 className="text-2xl font-bold">Configuration</h1>
|
||||
</div>
|
||||
<button
|
||||
onClick={handleApply}
|
||||
disabled={applying}
|
||||
className="flex items-center gap-1.5 px-3 py-1.5 text-sm rounded bg-blue-700 hover:bg-blue-600 text-white transition-colors disabled:opacity-40"
|
||||
>
|
||||
{applying ? <Loader2 size={14} className="animate-spin" /> : <Zap size={14} />}
|
||||
Apply All
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{message && (
|
||||
<div
|
||||
className={`mb-4 px-3 py-2 rounded text-sm ${
|
||||
message.type === "ok"
|
||||
? "bg-green-900/30 text-green-300 border border-green-800"
|
||||
: "bg-red-900/30 text-red-300 border border-red-800"
|
||||
}`}
|
||||
>
|
||||
<span className="flex items-center gap-1.5">
|
||||
{message.type === "ok" && <Check size={14} />}
|
||||
{message.text}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{applyResult && (
|
||||
<div className="mb-4 bg-[var(--card)] border border-[var(--border)] rounded-lg p-4 text-sm space-y-1">
|
||||
{applyResult.actions.map((a, i) => (
|
||||
<div key={i} className="text-green-400">{a}</div>
|
||||
))}
|
||||
{applyResult.errors.map((e, i) => (
|
||||
<div key={i} className="text-red-400">{e}</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{isLoading ? (
|
||||
<p className="text-[var(--muted)]">Loading entries...</p>
|
||||
) : (
|
||||
<div className="space-y-2">
|
||||
{components?.map((comp) => (
|
||||
<DeploymentEditor
|
||||
key={comp.id}
|
||||
deployment={comp}
|
||||
onSave={handleSave}
|
||||
onDelete={handleDelete}
|
||||
/>
|
||||
))}
|
||||
<AddDeployment
|
||||
existingNames={components?.map((c) => c.id) ?? []}
|
||||
onAdd={handleSave}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user