00466a9801
Introduces a manifest contract so feature containers self-describe their settings (JSON Schema + access rules). Backend and frontend gain generic plugin proxy and dynamic Extensions UI with zero feature-specific code. Doc-service is the first plugin consumer: exposes /plugin/manifest and /plugin/settings, adds a watchdog-based file watcher that auto-ingests PDFs from a mounted directory, maps subfolders to categories, supports AI-suggested folder/filename (user-confirmed), and enforces a no-remove policy. Access is gated by is_superuser or doc-service-admin group. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
64 lines
1.9 KiB
TypeScript
64 lines
1.9 KiB
TypeScript
import { useParams } from "react-router-dom";
|
|
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
|
|
import { getPluginManifest, getPluginSettings, updatePluginSettings } from "@/api/client";
|
|
import PluginSchemaForm from "@/components/PluginSchemaForm";
|
|
|
|
export default function PluginSettingsPage() {
|
|
const { id } = useParams<{ id: string }>();
|
|
const queryClient = useQueryClient();
|
|
|
|
const { data: manifest, isLoading: manifestLoading, isError: manifestError } = useQuery({
|
|
queryKey: ["plugin-manifest", id],
|
|
queryFn: () => getPluginManifest(id!),
|
|
enabled: !!id,
|
|
retry: false,
|
|
});
|
|
|
|
const { data: settings, isLoading: settingsLoading } = useQuery({
|
|
queryKey: ["plugin-settings", id],
|
|
queryFn: () => getPluginSettings(id!),
|
|
enabled: !!id && !!manifest,
|
|
});
|
|
|
|
const mutation = useMutation({
|
|
mutationFn: (values: Record<string, unknown>) => updatePluginSettings(id!, values),
|
|
onSuccess: () => {
|
|
queryClient.invalidateQueries({ queryKey: ["plugin-settings", id] });
|
|
},
|
|
});
|
|
|
|
if (manifestLoading || settingsLoading) {
|
|
return <p className="text-sm text-muted p-6">Loading…</p>;
|
|
}
|
|
|
|
if (manifestError || !manifest) {
|
|
return (
|
|
<p className="text-sm text-destructive p-6">
|
|
Plugin not found or you do not have access to its settings.
|
|
</p>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<div className="max-w-xl p-6 space-y-6">
|
|
<div>
|
|
<h1 className="text-xl font-semibold text-foreground">
|
|
{manifest.settings_schema.title ?? manifest.name}
|
|
</h1>
|
|
<p className="text-sm text-muted mt-1">
|
|
{manifest.name} · v{manifest.version}
|
|
</p>
|
|
</div>
|
|
|
|
<PluginSchemaForm
|
|
schema={manifest.settings_schema}
|
|
values={settings ?? {}}
|
|
onSave={(values) => mutation.mutate(values)}
|
|
isPending={mutation.isPending}
|
|
isError={mutation.isError}
|
|
isSuccess={mutation.isSuccess}
|
|
/>
|
|
</div>
|
|
);
|
|
}
|