From 0367563ea8f4960a8d569a08645ec90f00b908b1 Mon Sep 17 00:00:00 2001 From: ValueOn AG Date: Mon, 23 Mar 2026 00:05:24 +0100 Subject: [PATCH] tool fixes --- src/pages/billing/AdminSubscriptionsPage.tsx | 10 -- src/pages/billing/BillingAdmin.tsx | 13 +- .../workspace/WorkspaceGeneralSettings.tsx | 155 ++++++++++++++++++ .../views/workspace/WorkspaceSettingsPage.tsx | 11 +- 4 files changed, 165 insertions(+), 24 deletions(-) create mode 100644 src/pages/views/workspace/WorkspaceGeneralSettings.tsx diff --git a/src/pages/billing/AdminSubscriptionsPage.tsx b/src/pages/billing/AdminSubscriptionsPage.tsx index 7c16320..287b903 100644 --- a/src/pages/billing/AdminSubscriptionsPage.tsx +++ b/src/pages/billing/AdminSubscriptionsPage.tsx @@ -1,5 +1,4 @@ import React, { useCallback } from 'react'; -import { useNavigate } from 'react-router-dom'; import { FormGeneratorTable, type ColumnConfig } from '../../components/FormGenerator/FormGeneratorTable'; import { useAdminSubscriptions } from '../../hooks/useAdminSubscriptions'; import { useConfirm } from '../../hooks/useConfirm'; @@ -23,7 +22,6 @@ const _COLUMNS: ColumnConfig[] = [ ]; const AdminSubscriptionsPage: React.FC = () => { - const navigate = useNavigate(); const { confirm, ConfirmDialog } = useConfirm(); const { data: subscriptions, pagination, loading, refetch } = useAdminSubscriptions(); @@ -47,14 +45,6 @@ const AdminSubscriptionsPage: React.FC = () => {

Subscription-Übersicht

Alle Abonnements aller Mandanten

-
diff --git a/src/pages/billing/BillingAdmin.tsx b/src/pages/billing/BillingAdmin.tsx index 67e0cc9..d317f51 100644 --- a/src/pages/billing/BillingAdmin.tsx +++ b/src/pages/billing/BillingAdmin.tsx @@ -8,7 +8,7 @@ */ import React, { useState, useEffect, useCallback, useMemo } from 'react'; -import { useSearchParams, Link } from 'react-router-dom'; +import { useSearchParams } from 'react-router-dom'; import { useBillingAdmin, type BillingSettings, type AccountSummary, type MandateUserSummary } from '../../hooks/useBilling'; import type { CheckoutCreateRequest } from '../../api/billingApi'; import { useUserMandates, type Mandate as UserMandateRow } from '../../hooks/useUserMandates'; @@ -695,7 +695,7 @@ export const BillingAdmin: React.FC = () => { setStripeReturnMessage(null); }, [searchParams, setSearchParams]); - const showStripeForMandateAdmin = !isSysAdmin && !!selectedMandateId && !!settings; + const showStripeForMandateAdmin = !!selectedMandateId && !!settings; const _tabStyle = (isActive: boolean) => ({ padding: '8px 16px', @@ -719,15 +719,6 @@ export const BillingAdmin: React.FC = () => { Abrechnungseinstellungen, Guthaben und Abonnement pro Mandant

- {isSysAdmin && ( - - Alle Abonnements → - - )} diff --git a/src/pages/views/workspace/WorkspaceGeneralSettings.tsx b/src/pages/views/workspace/WorkspaceGeneralSettings.tsx new file mode 100644 index 0000000..901a8fb --- /dev/null +++ b/src/pages/views/workspace/WorkspaceGeneralSettings.tsx @@ -0,0 +1,155 @@ +/** + * WorkspaceGeneralSettings -- Per-user workspace settings (e.g. max agent rounds). + * + * The user can override the instance default. Setting a field to null reverts to the default. + */ + +import React, { useState, useEffect, useCallback } from 'react'; +import { useApiRequest } from '../../../hooks/useApi'; +import styles from './WorkspaceSettings.module.css'; + +interface GeneralSettingsProps { + instanceId: string; +} + +interface MaxAgentRoundsInfo { + effective: number; + userOverride: number | null; + instanceDefault: number; +} + +export const WorkspaceGeneralSettings: React.FC = ({ instanceId }) => { + const { request } = useApiRequest(); + + const [loading, setLoading] = useState(true); + const [saving, setSaving] = useState(false); + const [error, setError] = useState(null); + const [success, setSuccess] = useState(null); + + const [maxRoundsInfo, setMaxRoundsInfo] = useState({ + effective: 25, + userOverride: null, + instanceDefault: 25, + }); + const [inputValue, setInputValue] = useState(''); + + const _loadSettings = useCallback(async () => { + if (!instanceId) return; + setLoading(true); + try { + const data = await request({ + url: `/api/workspace/${instanceId}/settings/general`, + method: 'get', + }); + const info = (data as any)?.maxAgentRounds; + if (info) { + setMaxRoundsInfo(info); + setInputValue(info.userOverride != null ? String(info.userOverride) : ''); + } + } catch (err: any) { + setError(err?.message || 'Fehler beim Laden der Einstellungen'); + } finally { + setLoading(false); + } + }, [instanceId, request]); + + useEffect(() => { + _loadSettings(); + }, [_loadSettings]); + + const _handleSave = async () => { + setSaving(true); + setError(null); + setSuccess(null); + try { + const val = inputValue.trim() === '' ? null : parseInt(inputValue, 10); + if (val !== null && (isNaN(val) || val < 1 || val > 100)) { + setError('Wert muss zwischen 1 und 100 liegen.'); + setSaving(false); + return; + } + const data = await request({ + url: `/api/workspace/${instanceId}/settings/general`, + method: 'put', + data: { maxAgentRounds: val }, + }); + const info = (data as any)?.maxAgentRounds; + if (info) { + setMaxRoundsInfo(info); + setInputValue(info.userOverride != null ? String(info.userOverride) : ''); + } + setSuccess('Einstellungen gespeichert.'); + setTimeout(() => setSuccess(null), 3000); + } catch (err: any) { + setError(err?.message || 'Fehler beim Speichern'); + } finally { + setSaving(false); + } + }; + + const _handleReset = () => { + setInputValue(''); + }; + + if (loading) { + return
Lade Einstellungen...
; + } + + const hasOverride = inputValue.trim() !== ''; + + return ( +
+

Generelle Einstellungen

+ + {error &&
{error}
} + {success &&
{success}
} + +
+

Agenten-Konfiguration

+ +
+ +
+ setInputValue(e.target.value)} + placeholder={String(maxRoundsInfo.instanceDefault)} + min={1} + max={100} + /> + {hasOverride && ( + + )} +
+ + Standard der Instanz: {maxRoundsInfo.instanceDefault}. + {maxRoundsInfo.userOverride != null && ( + <> Ihr Override: {maxRoundsInfo.userOverride}. + )} + {' '}Effektiv: {maxRoundsInfo.effective}. + +
+
+ + +
+ ); +}; diff --git a/src/pages/views/workspace/WorkspaceSettingsPage.tsx b/src/pages/views/workspace/WorkspaceSettingsPage.tsx index 52ea9f7..d19e0df 100644 --- a/src/pages/views/workspace/WorkspaceSettingsPage.tsx +++ b/src/pages/views/workspace/WorkspaceSettingsPage.tsx @@ -8,21 +8,23 @@ import React, { useState } from 'react'; import { useInstanceId } from '../../../hooks/useCurrentInstance'; import { WorkspaceSettings } from './WorkspaceSettings'; +import { WorkspaceGeneralSettings } from './WorkspaceGeneralSettings'; -type SettingsTab = 'voice'; +type SettingsTab = 'general' | 'voice'; const _TABS: { key: SettingsTab; label: string }[] = [ + { key: 'general', label: 'Generelle Einstellungen' }, { key: 'voice', label: 'Sprache & Stimme' }, ]; export const WorkspaceSettingsPage: React.FC = () => { const instanceId = useInstanceId(); - const [activeTab, setActiveTab] = useState('voice'); + const [activeTab, setActiveTab] = useState('general'); if (!instanceId) { return (
- Keine Workspace-Instanz ausgewaehlt. + Keine Workspace-Instanz ausgewählt.
); } @@ -61,6 +63,9 @@ export const WorkspaceSettingsPage: React.FC = () => {
+ {activeTab === 'general' && ( + + )} {activeTab === 'voice' && ( )}