feat(ui): External-Agent-API + Besitzübertragung UI (UI-Backlog Module 15+16/16)
Check Cross-Plugin Imports / check (push) Has been cancelled
Check Cross-Plugin Imports / check (push) Has been cancelled
Modul 15 External-Agent (ai_assistant-Plugin, Manifest settings_page): - SettingsExternalAgents.tsx: Agentenliste mit curl-Snippets (run/status/stream), Copy-Buttons, Bearer-Token-Hinweis, Rate-Limit-Doku, Token-Link - api/externalAgent.ts (useAiAgents, buildCurlSnippets, curlCommand) - Manifest: settings_pages +external-agents (order 61, permission ai:read) - Komponenten-Map regeneriert (43) Modul 16 Ownership-Transfer (Core): - SettingsOwnership.tsx: Admin-Gate, From/To-User-Selects, 10 Entity-Type-Chips, ConfirmDialog, Ergebnis-Tabelle - api/ownership.ts (useTransferOwnership, OWNERSHIP_ENTITY_TYPES) - Route /settings/ownership + Nav-Eintrag i18n de/en +28 Keys. Vitest 12/12, tsc 0, Build OK, ruff OK, Manifest-Import OK
This commit is contained in:
@@ -0,0 +1,74 @@
|
||||
/**
|
||||
* External Agent API integration client (UI-Backlog module 15/16).
|
||||
*
|
||||
* Backend: /api/v1/external/agent (ai_assistant plugin, external_api.py).
|
||||
* - POST /{agent_id}/run → run agent, return full response (Bearer auth)
|
||||
* - GET /{agent_id}/status → agent config + run stats (Bearer auth)
|
||||
* - POST /{agent_id}/stream → SSE stream response (Bearer auth)
|
||||
*
|
||||
* These endpoints are Bearer-token-only (no session cookies) — they are
|
||||
* meant for EXTERNAL systems, not the SPA itself. The UI is therefore an
|
||||
* integration page: it lists the tenant's AI agents (via the existing
|
||||
* /ai/agents endpoint) and renders copy-paste curl snippets per agent.
|
||||
*/
|
||||
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { fetchAgents, type AIAgent } from '@/api/ai';
|
||||
|
||||
/** TanStack wrapper around the existing fetchAgents (session auth). */
|
||||
export function useAiAgents() {
|
||||
return useQuery({
|
||||
queryKey: ['ai-agents'],
|
||||
queryFn: () => fetchAgents(),
|
||||
});
|
||||
}
|
||||
|
||||
export interface CurlSnippet {
|
||||
method: 'POST' | 'GET';
|
||||
endpoint: string;
|
||||
body?: string;
|
||||
description: string;
|
||||
}
|
||||
|
||||
/** Base URL of this deployment (works for prod and local dev). */
|
||||
export function externalBaseUrl(): string {
|
||||
return `${window.location.origin}/api/v1/external/agent`;
|
||||
}
|
||||
|
||||
/** Build the copy-paste curl examples for one agent. */
|
||||
export function buildCurlSnippets(agentId: string): CurlSnippet[] {
|
||||
const base = externalBaseUrl();
|
||||
return [
|
||||
{
|
||||
method: 'POST',
|
||||
endpoint: `${base}/${agentId}/run`,
|
||||
body: JSON.stringify({ message: 'Hello, summarize my open tasks' }),
|
||||
description: 'runAgent',
|
||||
},
|
||||
{
|
||||
method: 'GET',
|
||||
endpoint: `${base}/${agentId}/status`,
|
||||
description: 'getStatus',
|
||||
},
|
||||
{
|
||||
method: 'POST',
|
||||
endpoint: `${base}/${agentId}/stream`,
|
||||
body: JSON.stringify({ message: 'Hello, stream me an answer' }),
|
||||
description: 'streamAgent (SSE)',
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/** Render a single-line curl command for a snippet. */
|
||||
export function curlCommand(snippet: CurlSnippet): string {
|
||||
const parts: string[] = [];
|
||||
parts.push(`curl -X ${snippet.method} '${snippet.endpoint}'`);
|
||||
parts.push(`-H 'Authorization: Bearer <API_TOKEN>'`);
|
||||
if (snippet.body) {
|
||||
parts.push(`-H 'Content-Type: application/json'`);
|
||||
parts.push(`-d '${snippet.body}'`);
|
||||
}
|
||||
return parts.join(' ');
|
||||
}
|
||||
|
||||
export type { AIAgent };
|
||||
@@ -0,0 +1,46 @@
|
||||
/**
|
||||
* Ownership transfer API client (UI-Backlog module 16/16).
|
||||
*
|
||||
* Backend: /api/v1/ownership (app/routes/owner_transfer.py, require_admin).
|
||||
* - POST /transfer → bulk-transfer records from one user to another
|
||||
*
|
||||
* Entity types: contacts, addresses, attachments, bank_accounts,
|
||||
* workflows, sequences, saved_filters, saved_views, webhooks,
|
||||
* notifications (mirrored from owner_transfer_service.ENTITY_TABLES).
|
||||
*/
|
||||
|
||||
import { useMutation } from '@tanstack/react-query';
|
||||
import { apiPost } from '@/api/client';
|
||||
|
||||
export const OWNERSHIP_ENTITY_TYPES = [
|
||||
'contacts',
|
||||
'addresses',
|
||||
'attachments',
|
||||
'bank_accounts',
|
||||
'workflows',
|
||||
'sequences',
|
||||
'saved_filters',
|
||||
'saved_views',
|
||||
'webhooks',
|
||||
'notifications',
|
||||
] as const;
|
||||
|
||||
export type OwnershipEntityType = (typeof OWNERSHIP_ENTITY_TYPES)[number];
|
||||
|
||||
export interface OwnershipTransferPayload {
|
||||
from_user_id: string;
|
||||
to_user_id: string;
|
||||
entity_types?: string[] | null;
|
||||
}
|
||||
|
||||
export interface OwnershipTransferResult {
|
||||
message: string;
|
||||
results: Record<string, number>;
|
||||
}
|
||||
|
||||
export function useTransferOwnership() {
|
||||
return useMutation({
|
||||
mutationFn: (data: OwnershipTransferPayload) =>
|
||||
apiPost<OwnershipTransferResult>('/ownership/transfer', data),
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user