Archived
Isolate providers per client: add provider targets and a Codex-side form
This commit is contained in:
1 parent
adc8234cc9
commit
d759fe5e5d
13 files changed
+595
-309
No files matched your search
+65
-10
@@ -1,25 +1,41 @@
|
||||
import { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { RotateCcw, Zap } from "lucide-react";
|
||||
import { Plus, RotateCcw, Trash2, Pencil, Zap } from "lucide-react";
|
||||
import type { Provider } from "@/lib/types";
|
||||
import { useProviders, useProviderMutations, useRestore, useStatus } from "@/lib/queries";
|
||||
import { useToast } from "@/components/toast";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import { ProviderForm, type ProviderSubmitInput } from "@/components/ProviderForm";
|
||||
|
||||
export default function Codex() {
|
||||
const { t } = useTranslation();
|
||||
const status = useStatus();
|
||||
const { data, isLoading, isError } = useProviders();
|
||||
const { data, isLoading, isError } = useProviders("codex");
|
||||
const mutations = useProviderMutations();
|
||||
const restore = useRestore();
|
||||
const { show } = useToast();
|
||||
const [injection, setInjection] = useState<Record<string, string> | null>(null);
|
||||
const [editing, setEditing] = useState<{ provider: Provider | null } | null>(null);
|
||||
|
||||
const providers = data?.items ?? [];
|
||||
const active = status.data?.active_codex_provider ?? null;
|
||||
|
||||
const submitForm = (input: ProviderSubmitInput) => {
|
||||
const target = editing?.provider ?? null;
|
||||
const onSuccess = () => {
|
||||
show(target ? t("providers.updated") : t("providers.created"));
|
||||
setEditing(null);
|
||||
};
|
||||
const onError = (err: Error) => show(err.message, "error");
|
||||
if (target) {
|
||||
mutations.update.mutate({ id: target.id, input }, { onSuccess, onError });
|
||||
} else {
|
||||
mutations.create.mutate(input, { onSuccess, onError });
|
||||
}
|
||||
};
|
||||
|
||||
const apply = (provider: Provider) => {
|
||||
mutations.apply.mutate(
|
||||
{ id: provider.id, target: "codex" },
|
||||
@@ -53,16 +69,32 @@ export default function Codex() {
|
||||
<div className="flex flex-col gap-5">
|
||||
<div className="flex items-center justify-between">
|
||||
<h2 className="text-lg font-semibold">{t("codex.title")}</h2>
|
||||
<Button variant="outline" size="sm" disabled={restore.isPending} onClick={restoreConfig}>
|
||||
{restore.isPending ? (
|
||||
<RotateCcw size={14} className="animate-spin" aria-hidden />
|
||||
) : (
|
||||
<Zap size={14} aria-hidden />
|
||||
)}
|
||||
{restore.isPending ? t("common.restoring") : t("codex.restore")}
|
||||
</Button>
|
||||
<div className="flex gap-2">
|
||||
<Button size="sm" onClick={() => setEditing({ provider: null })}>
|
||||
<Plus size={14} aria-hidden />
|
||||
{t("providers.add")}
|
||||
</Button>
|
||||
<Button variant="outline" size="sm" disabled={restore.isPending} onClick={restoreConfig}>
|
||||
{restore.isPending ? (
|
||||
<RotateCcw size={14} className="animate-spin" aria-hidden />
|
||||
) : (
|
||||
<Zap size={14} aria-hidden />
|
||||
)}
|
||||
{restore.isPending ? t("common.restoring") : t("codex.restore")}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{editing && (
|
||||
<ProviderForm
|
||||
target="codex"
|
||||
editing={editing.provider}
|
||||
pending={mutations.create.isPending || mutations.update.isPending}
|
||||
onSubmit={submitForm}
|
||||
onCancel={() => setEditing(null)}
|
||||
/>
|
||||
)}
|
||||
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>{t("codex.status")}</CardTitle>
|
||||
@@ -126,6 +158,29 @@ export default function Codex() {
|
||||
<Button variant="outline" size="sm" onClick={() => apply(provider)}>
|
||||
{t("codex.activate")}
|
||||
</Button>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
aria-label={t("common.edit")}
|
||||
onClick={() => setEditing({ provider })}
|
||||
>
|
||||
<Pencil size={14} aria-hidden />
|
||||
</Button>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
aria-label={t("common.delete")}
|
||||
onClick={() => {
|
||||
if (window.confirm(t("providers.deleteConfirm"))) {
|
||||
mutations.remove.mutate(provider.id, {
|
||||
onSuccess: () => show(t("providers.deleted")),
|
||||
onError: (err: Error) => show(err.message, "error"),
|
||||
});
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Trash2 size={14} aria-hidden />
|
||||
</Button>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
Reference in new issue
Block a user