mirror of
https://github.com/dograh-hq/dograh.git
synced 2026-07-04 10:52:17 +02:00
Initial Commit 🚀 🚀
This commit is contained in:
commit
4f2a629340
444 changed files with 76863 additions and 0 deletions
20
ui/src/app/looptalk/LoopTalkLayout.tsx
Normal file
20
ui/src/app/looptalk/LoopTalkLayout.tsx
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
import React, { ReactNode } from 'react'
|
||||
|
||||
import BaseHeader from '@/components/header/BaseHeader'
|
||||
|
||||
interface LoopTalkLayoutProps {
|
||||
children: ReactNode,
|
||||
headerActions?: ReactNode,
|
||||
backButton?: ReactNode,
|
||||
}
|
||||
|
||||
const LoopTalkLayout: React.FC<LoopTalkLayoutProps> = ({ children, headerActions, backButton }) => {
|
||||
return (
|
||||
<>
|
||||
<BaseHeader headerActions={headerActions} backButton={backButton} />
|
||||
{children}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default LoopTalkLayout
|
||||
126
ui/src/app/looptalk/[id]/page.tsx
Normal file
126
ui/src/app/looptalk/[id]/page.tsx
Normal file
|
|
@ -0,0 +1,126 @@
|
|||
import { ArrowLeft } from 'lucide-react';
|
||||
import Link from 'next/link';
|
||||
import { notFound } from 'next/navigation';
|
||||
import { Suspense } from 'react';
|
||||
|
||||
import { getTestSessionApiV1LooptalkTestSessionsTestSessionIdGet } from '@/client/sdk.gen';
|
||||
import { ConversationsList } from '@/components/looptalk/ConversationsList';
|
||||
import { LiveAudioPlayer } from '@/components/looptalk/LiveAudioPlayer';
|
||||
import { TestSessionControls } from '@/components/looptalk/TestSessionControls';
|
||||
import { TestSessionDetails } from '@/components/looptalk/TestSessionDetails';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { getServerAccessToken,getServerAuthProvider } from '@/lib/auth/server';
|
||||
import logger from '@/lib/logger';
|
||||
|
||||
import LoopTalkLayout from "../LoopTalkLayout";
|
||||
|
||||
interface PageProps {
|
||||
params: Promise<{
|
||||
id: string;
|
||||
}>;
|
||||
}
|
||||
|
||||
async function PageContent({ params }: PageProps) {
|
||||
const authProvider = getServerAuthProvider();
|
||||
const accessToken = await getServerAccessToken();
|
||||
|
||||
if (!accessToken) {
|
||||
const { redirect } = await import('next/navigation');
|
||||
if (authProvider === 'stack') {
|
||||
redirect('/');
|
||||
} else {
|
||||
// For OSS mode, this shouldn't happen as token is auto-generated
|
||||
return (
|
||||
<div className="text-red-500">
|
||||
Authentication required. Please refresh the page.
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const resolvedParams = await params;
|
||||
const testSessionId = parseInt(resolvedParams.id);
|
||||
const response = await getTestSessionApiV1LooptalkTestSessionsTestSessionIdGet({
|
||||
path: {
|
||||
test_session_id: testSessionId
|
||||
},
|
||||
headers: {
|
||||
'Authorization': `Bearer ${accessToken}`,
|
||||
},
|
||||
});
|
||||
|
||||
const testSession = response.data;
|
||||
|
||||
if (!testSession) {
|
||||
notFound();
|
||||
}
|
||||
|
||||
// Transform the API response to match our UI types
|
||||
const sessionForUI = {
|
||||
id: testSession.id,
|
||||
name: testSession.name,
|
||||
description: '', // API doesn't return description
|
||||
test_type: testSession.test_index !== null ? 'load_test' : 'single',
|
||||
status: testSession.status,
|
||||
actor_workflow_name: `Workflow ${testSession.actor_workflow_id}`, // We'll need to fetch actual names
|
||||
adversary_workflow_name: `Workflow ${testSession.adversary_workflow_id}`,
|
||||
created_at: testSession.created_at,
|
||||
updated_at: testSession.created_at, // API doesn't have updated_at
|
||||
test_metadata: testSession.config
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="container mx-auto px-4 py-8">
|
||||
<TestSessionDetails session={sessionForUI} />
|
||||
<TestSessionControls session={sessionForUI} />
|
||||
{/* Persistent Audio Player */}
|
||||
<div className="mt-6">
|
||||
<LiveAudioPlayer
|
||||
testSessionId={testSessionId}
|
||||
sessionStatus={testSession.status as 'pending' | 'running' | 'completed' | 'failed'}
|
||||
autoStart={true}
|
||||
/>
|
||||
</div>
|
||||
<div className="mt-8">
|
||||
<h2 className="text-xl font-bold mb-4">Conversations</h2>
|
||||
<ConversationsList testSessionId={testSessionId} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
} catch (err) {
|
||||
logger.error(`Error fetching test session: ${err}`);
|
||||
notFound();
|
||||
}
|
||||
}
|
||||
|
||||
function TestSessionLoading() {
|
||||
return (
|
||||
<div className="container mx-auto px-4 py-8">
|
||||
<div className="space-y-4">
|
||||
<div className="h-32 bg-gray-200 rounded-lg animate-pulse"></div>
|
||||
<div className="h-20 bg-gray-200 rounded-lg animate-pulse"></div>
|
||||
<div className="h-64 bg-gray-200 rounded-lg animate-pulse"></div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default function TestSessionPage({ params }: PageProps) {
|
||||
const backButton = (
|
||||
<Link href="/looptalk">
|
||||
<Button variant="ghost" size="sm">
|
||||
<ArrowLeft className="h-4 w-4 mr-2" />
|
||||
Back to Test Sessions
|
||||
</Button>
|
||||
</Link>
|
||||
);
|
||||
|
||||
return (
|
||||
<LoopTalkLayout backButton={backButton}>
|
||||
<Suspense fallback={<TestSessionLoading />}>
|
||||
<PageContent params={params} />
|
||||
</Suspense>
|
||||
</LoopTalkLayout>
|
||||
);
|
||||
}
|
||||
77
ui/src/app/looptalk/page.tsx
Normal file
77
ui/src/app/looptalk/page.tsx
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
import { Suspense } from 'react';
|
||||
|
||||
import { CreateTestSessionButton } from '@/components/looptalk/CreateTestSessionButton';
|
||||
import { LoopTalkTestSessionsList } from '@/components/looptalk/LoopTalkTestSessionsList';
|
||||
import { getServerAuthProvider, isServerAuthenticated } from '@/lib/auth/server';
|
||||
|
||||
import LoopTalkLayout from "./LoopTalkLayout";
|
||||
|
||||
async function PageContent() {
|
||||
const authProvider = getServerAuthProvider();
|
||||
const isAuthenticated = await isServerAuthenticated();
|
||||
|
||||
if (authProvider === 'stack' && !isAuthenticated) {
|
||||
const { redirect } = await import('next/navigation');
|
||||
redirect('/');
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="container mx-auto px-4 py-8">
|
||||
{/* Active Tests Section */}
|
||||
<div className="mb-12">
|
||||
<div className="flex justify-between items-center mb-6">
|
||||
<h2 className="text-2xl font-bold">Active Tests</h2>
|
||||
</div>
|
||||
<LoopTalkTestSessionsList status="active" />
|
||||
</div>
|
||||
|
||||
{/* Test Sessions Section */}
|
||||
<div className="mb-6">
|
||||
<div className="flex justify-between items-center mb-6">
|
||||
<h1 className="text-2xl font-bold">Test Sessions</h1>
|
||||
<CreateTestSessionButton />
|
||||
</div>
|
||||
<LoopTalkTestSessionsList />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function LoopTalkLoading() {
|
||||
return (
|
||||
<div className="container mx-auto px-4 py-8">
|
||||
{/* Active Tests Section Loading */}
|
||||
<div className="mb-12">
|
||||
<div className="h-8 w-48 bg-gray-200 rounded mb-6"></div>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
{Array.from({ length: 3 }, (_, i) => (
|
||||
<div key={i} className="bg-gray-200 rounded-lg h-40"></div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Test Sessions Section Loading */}
|
||||
<div className="mb-6">
|
||||
<div className="flex justify-between items-center mb-6">
|
||||
<div className="h-8 w-48 bg-gray-200 rounded"></div>
|
||||
<div className="h-10 w-32 bg-gray-200 rounded"></div>
|
||||
</div>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
{Array.from({ length: 6 }, (_, i) => (
|
||||
<div key={i} className="bg-gray-200 rounded-lg h-32"></div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default function LoopTalkPage() {
|
||||
return (
|
||||
<LoopTalkLayout>
|
||||
<Suspense fallback={<LoopTalkLoading />}>
|
||||
<PageContent />
|
||||
</Suspense>
|
||||
</LoopTalkLayout>
|
||||
);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue