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 = () => {
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' && (
)}