admin pages streamlined

This commit is contained in:
ValueOn AG 2026-02-04 14:09:51 +01:00
parent 932bc3e11b
commit ff1caba925
14 changed files with 82 additions and 354 deletions

View file

@ -43,15 +43,9 @@ import { GDPRPage } from './pages/GDPR';
import { FeatureViewPage } from './pages/FeatureView'; import { FeatureViewPage } from './pages/FeatureView';
import { AccessManagementHub, AdminMandatesPage, AdminUsersPage, AdminUserMandatesPage, AdminFeatureAccessPage, AdminInvitationsPage, AdminMandateRolesPage, AdminFeatureRolesPage, AdminFeatureInstanceUsersPage, AdminMandateRolePermissionsPage, AdminUserAccessOverviewPage } from './pages/admin'; import { AccessManagementHub, AdminMandatesPage, AdminUsersPage, AdminUserMandatesPage, AdminFeatureAccessPage, AdminInvitationsPage, AdminMandateRolesPage, AdminFeatureRolesPage, AdminFeatureInstanceUsersPage, AdminMandateRolePermissionsPage, AdminUserAccessOverviewPage } from './pages/admin';
// Workflow Pages (global)
import { PlaygroundPage, WorkflowsPage, AutomationsPage, AutomationTemplatesPage } from './pages/workflows';
// Basedata Pages (global) // Basedata Pages (global)
import { PromptsPage, FilesPage, ConnectionsPage } from './pages/basedata'; import { PromptsPage, FilesPage, ConnectionsPage } from './pages/basedata';
// Migrate Pages (temporary - to be migrated to feature instances)
import { PekPage, SpeechPage } from './pages/migrate';
function App() { function App() {
// Load saved theme preference and set app name on app mount // Load saved theme preference and set app name on app mount
useEffect(() => { useEffect(() => {
@ -107,16 +101,6 @@ function App() {
<Route path="settings" element={<SettingsPage />} /> <Route path="settings" element={<SettingsPage />} />
<Route path="gdpr" element={<GDPRPage />} /> <Route path="gdpr" element={<GDPRPage />} />
{/* ============================================== */}
{/* WORKFLOWS ROUTES (global) */}
{/* ============================================== */}
<Route path="workflows">
<Route path="playground" element={<PlaygroundPage />} />
<Route path="list" element={<WorkflowsPage />} />
<Route path="automations" element={<AutomationsPage />} />
<Route path="automation-templates" element={<AutomationTemplatesPage />} />
</Route>
{/* ============================================== */} {/* ============================================== */}
{/* BASISDATEN ROUTES (global) */} {/* BASISDATEN ROUTES (global) */}
{/* ============================================== */} {/* ============================================== */}
@ -126,13 +110,6 @@ function App() {
<Route path="connections" element={<ConnectionsPage />} /> <Route path="connections" element={<ConnectionsPage />} />
</Route> </Route>
{/* ============================================== */}
{/* MIGRATE TO FEATURES (temporary) */}
{/* ============================================== */}
<Route path="chatbot" element={<Navigate to="/" replace />} />
<Route path="pek" element={<PekPage />} />
<Route path="speech" element={<SpeechPage />} />
{/* ============================================== */} {/* ============================================== */}
{/* FEATURE-INSTANZ ROUTES */} {/* FEATURE-INSTANZ ROUTES */}
{/* /mandates/:mandateId/:featureCode/:instanceId */} {/* /mandates/:mandateId/:featureCode/:instanceId */}
@ -159,6 +136,15 @@ function App() {
<Route path="projects" element={<FeatureViewPage view="projects" />} /> <Route path="projects" element={<FeatureViewPage view="projects" />} />
<Route path="parcels" element={<FeatureViewPage view="parcels" />} /> <Route path="parcels" element={<FeatureViewPage view="parcels" />} />
{/* Chat Playground Feature Views */}
<Route path="playground" element={<FeatureViewPage view="playground" />} />
<Route path="workflows" element={<FeatureViewPage view="workflows" />} />
{/* Automation Feature Views */}
<Route path="definitions" element={<FeatureViewPage view="definitions" />} />
<Route path="templates" element={<FeatureViewPage view="templates" />} />
<Route path="logs" element={<FeatureViewPage view="logs" />} />
{/* Catch-all für unbekannte Sub-Pfade */} {/* Catch-all für unbekannte Sub-Pfade */}
<Route path="*" element={<FeatureViewPage view="not-found" />} /> <Route path="*" element={<FeatureViewPage view="not-found" />} />
</Route> </Route>

View file

@ -231,17 +231,18 @@ export async function fetchWorkflowLogs(
/** /**
* Fetch unified chat data (messages, logs, stats, documents) * Fetch unified chat data (messages, logs, stats, documents)
* Endpoint: GET /api/chat/playground/{workflowId}/chatData * Endpoint: GET /api/chatplayground/{instanceId}/{workflowId}/chatData
* Query params: afterTimestamp (optional) - fetch only data created after this time * Query params: afterTimestamp (optional) - fetch only data created after this time
*/ */
export async function fetchChatData( export async function fetchChatData(
request: ApiRequestFunction, request: ApiRequestFunction,
instanceId: string,
workflowId: string, workflowId: string,
afterTimestamp?: number afterTimestamp?: number
): Promise<ChatDataResponse> { ): Promise<ChatDataResponse> {
const params = afterTimestamp ? { afterTimestamp: afterTimestamp.toString() } : undefined; const params = afterTimestamp ? { afterTimestamp: afterTimestamp.toString() } : undefined;
const requestConfig = { const requestConfig = {
url: `/api/chat/playground/${workflowId}/chatData`, url: `/api/chatplayground/${instanceId}/${workflowId}/chatData`,
method: 'get' as const, method: 'get' as const,
params params
}; };
@ -314,11 +315,12 @@ export async function fetchChatData(
/** /**
* Start a new workflow or continue an existing one * Start a new workflow or continue an existing one
* Endpoint: POST /api/chat/playground/start * Endpoint: POST /api/chatplayground/{instanceId}/start
* Query params: workflowId (optional), workflowMode (default: "Actionplan") * Query params: workflowId (optional), workflowMode (default: "Dynamic")
*/ */
export async function startWorkflowApi( export async function startWorkflowApi(
request: ApiRequestFunction, request: ApiRequestFunction,
instanceId: string,
workflowData: StartWorkflowRequest, workflowData: StartWorkflowRequest,
options?: { workflowId?: string; workflowMode?: 'Dynamic' | 'Automation' } options?: { workflowId?: string; workflowMode?: 'Dynamic' | 'Automation' }
): Promise<StartWorkflowResponse> { ): Promise<StartWorkflowResponse> {
@ -345,7 +347,7 @@ export async function startWorkflowApi(
}; };
const requestConfig = { const requestConfig = {
url: '/api/chat/playground/start', url: `/api/chatplayground/${instanceId}/start`,
method: 'post' as const, method: 'post' as const,
data: requestBody, data: requestBody,
params: params // Always include workflowMode params: params // Always include workflowMode
@ -368,14 +370,15 @@ export async function startWorkflowApi(
/** /**
* Stop a running workflow * Stop a running workflow
* Endpoint: POST /api/chat/playground/{workflowId}/stop * Endpoint: POST /api/chatplayground/{instanceId}/{workflowId}/stop
*/ */
export async function stopWorkflowApi( export async function stopWorkflowApi(
request: ApiRequestFunction, request: ApiRequestFunction,
instanceId: string,
workflowId: string workflowId: string
): Promise<void> { ): Promise<void> {
await request({ await request({
url: `/api/chat/playground/${workflowId}/stop`, url: `/api/chatplayground/${instanceId}/${workflowId}/stop`,
method: 'post' method: 'post'
}); });
} }

View file

@ -23,7 +23,7 @@ export interface WorkflowFile {
source?: 'user_uploaded' | 'ai_created'; source?: 'user_uploaded' | 'ai_created';
} }
export function useDashboardInputForm() { export function useDashboardInputForm(instanceId: string) {
const [inputValue, setInputValue] = useState<string>(''); const [inputValue, setInputValue] = useState<string>('');
const [pendingFiles, setPendingFiles] = useState<WorkflowFile[]>([]); const [pendingFiles, setPendingFiles] = useState<WorkflowFile[]>([]);
const [isFileAttachmentPopupOpen, setIsFileAttachmentPopupOpen] = useState(false); const [isFileAttachmentPopupOpen, setIsFileAttachmentPopupOpen] = useState(false);
@ -54,7 +54,7 @@ export function useDashboardInputForm() {
resetWorkflow, resetWorkflow,
selectWorkflow, selectWorkflow,
setWorkflowStatusOptimistic setWorkflowStatusOptimistic
} = useWorkflowLifecycle(); } = useWorkflowLifecycle(instanceId);
// Dashboard log tree hook // Dashboard log tree hook
const { const {
@ -824,7 +824,7 @@ export function useDashboardInputForm() {
}; };
} }
export function createDashboardHook() { export function createDashboardHook(instanceId: string) {
return () => useDashboardInputForm(); return () => useDashboardInputForm(instanceId);
} }

View file

@ -18,7 +18,7 @@ interface UnifiedChatDataItem {
createdAt: number; createdAt: number;
} }
export function useWorkflowLifecycle() { export function useWorkflowLifecycle(instanceId: string) {
const [workflowId, setWorkflowId] = useState<string | null>(null); const [workflowId, setWorkflowId] = useState<string | null>(null);
const [workflowStatus, setWorkflowStatus] = useState<string>('idle'); const [workflowStatus, setWorkflowStatus] = useState<string>('idle');
const [currentRound, setCurrentRound] = useState<number | undefined>(undefined); const [currentRound, setCurrentRound] = useState<number | undefined>(undefined);
@ -341,7 +341,7 @@ export function useWorkflowLifecycle() {
} }
// Fetch unified chat data // Fetch unified chat data
const chatData = await fetchChatData(request, id, afterTimestamp); const chatData = await fetchChatData(request, instanceId, id, afterTimestamp);
console.log('📊 Processed chat data:', { console.log('📊 Processed chat data:', {
messagesCount: chatData.messages?.length || 0, messagesCount: chatData.messages?.length || 0,
@ -419,7 +419,7 @@ export function useWorkflowLifecycle() {
// Reset lastRenderedTimestamp to fetch all historical data // Reset lastRenderedTimestamp to fetch all historical data
lastRenderedTimestampRef.current = null; lastRenderedTimestampRef.current = null;
try { try {
const chatData = await fetchChatData(request, id, undefined); const chatData = await fetchChatData(request, instanceId, id, undefined);
console.log('📥 loadWorkflowData: Fetched unified chat data:', { console.log('📥 loadWorkflowData: Fetched unified chat data:', {
messagesCount: chatData.messages?.length || 0, messagesCount: chatData.messages?.length || 0,
logsCount: chatData.logs?.length || 0 logsCount: chatData.logs?.length || 0
@ -514,7 +514,7 @@ export function useWorkflowLifecycle() {
options?: { workflowId?: string; workflowMode?: 'Dynamic' | 'Automation' } options?: { workflowId?: string; workflowMode?: 'Dynamic' | 'Automation' }
) => { ) => {
try { try {
const result = await startWorkflow(workflowData, options); const result = await startWorkflow(instanceId, workflowData, options);
if (result.success && result.data) { if (result.success && result.data) {
const workflow = result.data as Workflow; const workflow = result.data as Workflow;
@ -529,7 +529,7 @@ export function useWorkflowLifecycle() {
} catch (error: any) { } catch (error: any) {
return { success: false, error: error.message || 'Failed to start workflow' }; return { success: false, error: error.message || 'Failed to start workflow' };
} }
}, [startWorkflow, updateWorkflowStatus]); }, [instanceId, startWorkflow, updateWorkflowStatus]);
const handleStopWorkflow = useCallback(async () => { const handleStopWorkflow = useCallback(async () => {
if (!workflowId) { if (!workflowId) {
@ -537,7 +537,7 @@ export function useWorkflowLifecycle() {
} }
try { try {
const result = await stopWorkflow(workflowId); const result = await stopWorkflow(instanceId, workflowId);
if (result.success) { if (result.success) {
updateWorkflowStatus('stopped'); updateWorkflowStatus('stopped');
@ -548,7 +548,7 @@ export function useWorkflowLifecycle() {
} catch (error: any) { } catch (error: any) {
return { success: false, error: error.message || 'Failed to stop workflow' }; return { success: false, error: error.message || 'Failed to stop workflow' };
} }
}, [workflowId, stopWorkflow, updateWorkflowStatus]); }, [instanceId, workflowId, stopWorkflow, updateWorkflowStatus]);
const resetWorkflow = useCallback(() => { const resetWorkflow = useCallback(() => {
setWorkflowId(null); setWorkflowId(null);
@ -597,7 +597,7 @@ export function useWorkflowLifecycle() {
// Always fetch unified chat data to get all messages and logs (regardless of status) // Always fetch unified chat data to get all messages and logs (regardless of status)
// This ensures completed workflows also show their logs // This ensures completed workflows also show their logs
try { try {
const chatData = await fetchChatData(request, workflowIdToSelect, undefined); const chatData = await fetchChatData(request, instanceId, workflowIdToSelect, undefined);
console.log('📥 selectWorkflow: Fetched unified chat data:', { console.log('📥 selectWorkflow: Fetched unified chat data:', {
messagesCount: chatData.messages?.length || 0, messagesCount: chatData.messages?.length || 0,
logsCount: chatData.logs?.length || 0, logsCount: chatData.logs?.length || 0,

View file

@ -226,7 +226,7 @@ export function useUserMandates() {
}, []); }, []);
/** /**
* Fetch all available roles (global and mandate-specific) * Fetch all available roles (global and mandate-specific, excluding feature-instance roles)
*/ */
const fetchRoles = useCallback(async (mandateId?: string): Promise<Role[]> => { const fetchRoles = useCallback(async (mandateId?: string): Promise<Role[]> => {
try { try {
@ -238,13 +238,15 @@ export function useUserMandates() {
roles = response.data; roles = response.data;
} }
// Filter to global roles and roles for this mandate // Filter to global roles and mandate-specific roles only
// Exclude feature-instance roles (they have featureInstanceId set)
if (mandateId) { if (mandateId) {
return roles.filter(r => return roles.filter(r =>
!r.mandateId || r.mandateId === mandateId !r.featureInstanceId && (!r.mandateId || r.mandateId === mandateId)
); );
} }
return roles; // Without mandateId, return only global roles (no mandateId and no featureInstanceId)
return roles.filter(r => !r.mandateId && !r.featureInstanceId);
} catch (err: any) { } catch (err: any) {
console.error('Error fetching roles:', err); console.error('Error fetching roles:', err);
return []; return [];

View file

@ -432,6 +432,7 @@ export function useWorkflowOperations() {
}; };
const startWorkflow = async ( const startWorkflow = async (
instanceId: string,
workflowData: StartWorkflowRequest, workflowData: StartWorkflowRequest,
options?: { workflowId?: string; workflowMode?: 'Dynamic' | 'Automation' } options?: { workflowId?: string; workflowMode?: 'Dynamic' | 'Automation' }
) => { ) => {
@ -439,7 +440,7 @@ export function useWorkflowOperations() {
setStartingWorkflow(true); setStartingWorkflow(true);
try { try {
const response = await startWorkflowApi(request, workflowData, options); const response = await startWorkflowApi(request, instanceId, workflowData, options);
return { success: true, data: response }; return { success: true, data: response };
} catch (error: any) { } catch (error: any) {
const errorMessage = error.message || 'Failed to start workflow'; const errorMessage = error.message || 'Failed to start workflow';
@ -450,12 +451,12 @@ export function useWorkflowOperations() {
} }
}; };
const stopWorkflow = async (workflowId: string) => { const stopWorkflow = async (instanceId: string, workflowId: string) => {
setStopError(null); setStopError(null);
setStoppingWorkflows(prev => new Set(prev).add(workflowId)); setStoppingWorkflows(prev => new Set(prev).add(workflowId));
try { try {
await stopWorkflowApi(request, workflowId); await stopWorkflowApi(request, instanceId, workflowId);
return { success: true }; return { success: true };
} catch (error: any) { } catch (error: any) {
const errorMessage = error.message || 'Failed to stop workflow'; const errorMessage = error.message || 'Failed to stop workflow';

View file

@ -25,6 +25,12 @@ import { ChatbotConversationsView } from './views/chatbot/ChatbotConversationsVi
// RealEstate Views // RealEstate Views
import { RealEstatePekView, RealEstateProjectsView, RealEstateParcelsView, RealEstateInstanceRolesPlaceholder } from './views/realestate'; import { RealEstatePekView, RealEstateProjectsView, RealEstateParcelsView, RealEstateInstanceRolesPlaceholder } from './views/realestate';
// Chat Playground Views (reusing existing workflow pages)
import { PlaygroundPage, WorkflowsPage } from './workflows';
// Automation Views (reusing existing workflow pages)
import { AutomationsPage, AutomationTemplatesPage } from './workflows';
import styles from './FeatureView.module.css'; import styles from './FeatureView.module.css';
// ============================================================================= // =============================================================================
@ -103,6 +109,15 @@ const VIEW_COMPONENTS: Record<string, Record<string, ViewComponent>> = {
parcels: RealEstateParcelsView, parcels: RealEstateParcelsView,
'instance-roles': RealEstateInstanceRolesPlaceholder, 'instance-roles': RealEstateInstanceRolesPlaceholder,
}, },
chatplayground: {
playground: PlaygroundPage,
workflows: WorkflowsPage,
},
automation: {
definitions: AutomationsPage,
templates: AutomationTemplatesPage,
logs: () => <PlaceholderView title="Execution Logs" description="Automatisierungs-Ausführungsprotokolle" />,
},
}; };
// ============================================================================= // =============================================================================

View file

@ -1,223 +0,0 @@
/* MigratePages.module.css - Styles for migrate-to-feature pages */
.page {
padding: 2rem;
max-width: 1200px;
margin: 0 auto;
height: calc(100vh - 4rem);
display: flex;
flex-direction: column;
}
.header {
margin-bottom: 1.5rem;
}
.header h1 {
font-size: 1.75rem;
font-weight: 600;
color: var(--color-text-primary, #1a1a2e);
margin: 0 0 0.5rem 0;
}
.subtitle {
color: var(--color-text-secondary, #6b7280);
margin: 0;
display: flex;
align-items: center;
gap: 0.75rem;
}
.migrateTag {
display: inline-block;
padding: 0.25rem 0.5rem;
background: var(--color-warning-bg, #fef3c7);
color: var(--color-warning, #d97706);
font-size: 0.65rem;
font-weight: 700;
border-radius: 4px;
text-transform: uppercase;
letter-spacing: 0.05em;
}
.content {
flex: 1;
display: flex;
align-items: center;
justify-content: center;
}
/* Placeholder for migrate pages */
.placeholder {
text-align: center;
padding: 3rem;
background: var(--color-surface, #ffffff);
border: 2px dashed var(--color-border, #e5e7eb);
border-radius: 12px;
max-width: 500px;
}
.placeholderIcon {
font-size: 4rem;
margin-bottom: 1rem;
}
.placeholder h2 {
margin: 0 0 1rem 0;
color: var(--color-text-primary, #1a1a2e);
}
.placeholder p {
color: var(--color-text-secondary, #6b7280);
margin: 0 0 0.5rem 0;
}
.hint {
font-size: 0.875rem;
color: var(--color-text-tertiary, #9ca3af);
margin-top: 1rem !important;
}
/* Chat container for ChatbotPage */
.chatContainer {
flex: 1;
display: flex;
flex-direction: column;
background: var(--color-surface, #ffffff);
border: 1px solid var(--color-border, #e5e7eb);
border-radius: 8px;
overflow: hidden;
}
.messagesArea {
flex: 1;
overflow-y: auto;
padding: 1rem;
display: flex;
flex-direction: column;
gap: 1rem;
}
.emptyChat {
flex: 1;
display: flex;
align-items: center;
justify-content: center;
color: var(--color-text-secondary, #6b7280);
}
.message {
max-width: 70%;
padding: 0.75rem 1rem;
border-radius: 12px;
}
.message.user {
align-self: flex-end;
background: var(--color-primary, #4f46e5);
color: white;
border-bottom-right-radius: 4px;
}
.message.assistant {
align-self: flex-start;
background: var(--color-surface-secondary, #f3f4f6);
color: var(--color-text-primary, #1a1a2e);
border-bottom-left-radius: 4px;
}
.message.system {
align-self: center;
background: var(--color-warning-bg, #fef3c7);
color: var(--color-warning, #d97706);
font-size: 0.875rem;
}
.messageContent {
word-wrap: break-word;
}
.messageTime {
font-size: 0.7rem;
opacity: 0.7;
margin-top: 0.25rem;
}
/* Typing indicator */
.typing {
display: flex;
gap: 4px;
padding: 0.5rem 0;
}
.typing span {
width: 8px;
height: 8px;
background: var(--color-text-secondary, #6b7280);
border-radius: 50%;
animation: typing 1s infinite;
}
.typing span:nth-child(2) {
animation-delay: 0.2s;
}
.typing span:nth-child(3) {
animation-delay: 0.4s;
}
@keyframes typing {
0%, 100% {
opacity: 0.3;
transform: scale(0.8);
}
50% {
opacity: 1;
transform: scale(1);
}
}
/* Input area */
.inputArea {
display: flex;
gap: 0.5rem;
padding: 1rem;
border-top: 1px solid var(--color-border, #e5e7eb);
background: var(--color-surface-secondary, #f9fafb);
}
.chatInput {
flex: 1;
padding: 0.75rem 1rem;
border: 1px solid var(--color-border, #e5e7eb);
border-radius: 24px;
font-size: 0.875rem;
background: var(--color-surface, #ffffff);
}
.chatInput:focus {
outline: none;
border-color: var(--color-primary, #4f46e5);
box-shadow: 0 0 0 3px var(--color-primary-light, rgba(79, 70, 229, 0.1));
}
.sendButton {
padding: 0.75rem 1.5rem;
background: var(--color-primary, #4f46e5);
color: white;
border: none;
border-radius: 24px;
font-size: 0.875rem;
font-weight: 500;
cursor: pointer;
transition: background 0.2s;
}
.sendButton:hover:not(:disabled) {
background: var(--color-primary-dark, #4338ca);
}
.sendButton:disabled {
opacity: 0.5;
cursor: not-allowed;
}

View file

@ -1,39 +0,0 @@
/**
* PekPage
*
* PEK (Projekt-Entwicklungs-Koordination) page - temporary global page.
* TODO: Migrate to feature instance.
*/
import React from 'react';
import styles from './MigratePages.module.css';
export const PekPage: React.FC = () => {
return (
<div className={styles.page}>
<header className={styles.header}>
<h1>PEK</h1>
<p className={styles.subtitle}>
<span className={styles.migrateTag}>MIGRATE TO FEATURE</span>
Projekt-Entwicklungs-Koordination
</p>
</header>
<main className={styles.content}>
<div className={styles.placeholder}>
<div className={styles.placeholderIcon}>📊</div>
<h2>PEK-Modul</h2>
<p>
Dieses Modul wird zu einer Feature-Instanz migriert.
</p>
<p className={styles.hint}>
Nach der Migration wird PEK als Feature pro Mandant verfügbar sein,
mit instanz-spezifischen Daten und Berechtigungen.
</p>
</div>
</main>
</div>
);
};
export default PekPage;

View file

@ -1,39 +0,0 @@
/**
* SpeechPage
*
* Speech recognition and transcription page - temporary global page.
* TODO: Migrate to feature instance.
*/
import React from 'react';
import styles from './MigratePages.module.css';
export const SpeechPage: React.FC = () => {
return (
<div className={styles.page}>
<header className={styles.header}>
<h1>Speech</h1>
<p className={styles.subtitle}>
<span className={styles.migrateTag}>MIGRATE TO FEATURE</span>
Spracherkennung und Transkription
</p>
</header>
<main className={styles.content}>
<div className={styles.placeholder}>
<div className={styles.placeholderIcon}>🎤</div>
<h2>Speech-Modul</h2>
<p>
Dieses Modul wird zu einer Feature-Instanz migriert.
</p>
<p className={styles.hint}>
Nach der Migration wird Speech als Feature pro Mandant verfügbar sein,
mit instanz-spezifischen Transkriptionen und Einstellungen.
</p>
</div>
</main>
</div>
);
};
export default SpeechPage;

View file

@ -1,2 +0,0 @@
export { PekPage } from './PekPage';
export { SpeechPage } from './SpeechPage';

View file

@ -426,7 +426,7 @@ export const AutomationsPage: React.FC = () => {
try { try {
await request({ await request({
url: `/api/chat/playground/${executionModal.workflowId}/stop`, url: `/api/workflows/${executionModal.workflowId}/stop`,
method: 'post', method: 'post',
}); });

View file

@ -11,6 +11,7 @@ import { useSearchParams } from 'react-router-dom';
import { useDashboardInputForm } from '../../hooks/usePlayground'; import { useDashboardInputForm } from '../../hooks/usePlayground';
import { useResizablePanels } from '../../hooks/useResizablePanels'; import { useResizablePanels } from '../../hooks/useResizablePanels';
import { usePrompts } from '../../hooks/usePrompts'; import { usePrompts } from '../../hooks/usePrompts';
import { useCurrentInstance } from '../../hooks/useCurrentInstance';
import { FaComment, FaTasks, FaPaperPlane, FaStop, FaFile, FaPlus, FaMicrophone, FaSquare, FaFileAlt } from 'react-icons/fa'; import { FaComment, FaTasks, FaPaperPlane, FaStop, FaFile, FaPlus, FaMicrophone, FaSquare, FaFileAlt } from 'react-icons/fa';
import { useToast } from '../../contexts/ToastContext'; import { useToast } from '../../contexts/ToastContext';
import { useVoiceLanguage, VoiceLanguageSelect, Messages } from '../../components/UiComponents'; import { useVoiceLanguage, VoiceLanguageSelect, Messages } from '../../components/UiComponents';
@ -23,8 +24,12 @@ export const PlaygroundPage: React.FC = () => {
const [searchParams] = useSearchParams(); const [searchParams] = useSearchParams();
const urlWorkflowId = searchParams.get('workflowId'); const urlWorkflowId = searchParams.get('workflowId');
// Get feature instance context
const { instance } = useCurrentInstance();
const instanceId = instance?.id || '';
// Main hook for input form and data // Main hook for input form and data
const hookData = useDashboardInputForm(); const hookData = useDashboardInputForm(instanceId);
const { const {
inputValue, inputValue,
onInputChange, onInputChange,

View file

@ -240,6 +240,25 @@ export const FEATURE_REGISTRY: Record<string, FeatureConfig> = {
{ code: 'instance-roles', label: { de: 'Rollen & Rechte', en: 'Roles & Permissions' }, path: 'instance-roles', adminOnly: true }, { code: 'instance-roles', label: { de: 'Rollen & Rechte', en: 'Roles & Permissions' }, path: 'instance-roles', adminOnly: true },
] ]
}, },
chatplayground: {
code: 'chatplayground',
label: { de: 'Chat Playground', en: 'Chat Playground' },
icon: 'message',
views: [
{ code: 'playground', label: { de: 'Playground', en: 'Playground' }, path: 'playground' },
{ code: 'workflows', label: { de: 'Workflows', en: 'Workflows' }, path: 'workflows' },
]
},
automation: {
code: 'automation',
label: { de: 'Automatisierung', en: 'Automation' },
icon: 'settings',
views: [
{ code: 'definitions', label: { de: 'Definitionen', en: 'Definitions' }, path: 'definitions' },
{ code: 'templates', label: { de: 'Vorlagen', en: 'Templates' }, path: 'templates' },
{ code: 'logs', label: { de: 'Protokolle', en: 'Logs' }, path: 'logs' },
]
},
}; };
// ============================================================================= // =============================================================================