Files
Business-Management/frontend/src/pages/PluginSettingsPage.tsx
T
curo1305 00466a9801 Add generic plugin architecture and watch-directory feature
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>
2026-04-18 02:09:50 +02:00

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>
);
}