feat: Implement structured message parsing and display in MessageViewer component

- Added MessageViewer component to display parsed messages in a chat-like format.
- Introduced new Message table in the database to store individual messages with timestamps, roles, and content.
- Updated Session model to include a relation to parsed messages.
- Created transcript parsing logic to convert raw transcripts into structured messages.
- Enhanced processing scheduler to handle sessions with parsed messages.
- Updated API endpoints to return parsed messages alongside session details.
- Added manual trigger commands for session refresh, transcript parsing, and processing.
- Improved user experience with color-coded message roles and timestamps in the UI.
- Documented the new scheduler workflow and transcript parsing implementation.
This commit is contained in:
Max Kowalski
2025-06-25 17:45:08 +02:00
parent 3196dabdf2
commit a9e4145001
20 changed files with 1043 additions and 90 deletions

View File

@ -0,0 +1,75 @@
"use client";
import { Message } from "../lib/types";
interface MessageViewerProps {
messages: Message[];
}
/**
* Component to display parsed messages in a chat-like format
*/
export default function MessageViewer({ messages }: MessageViewerProps) {
if (!messages || messages.length === 0) {
return (
<div className="bg-white p-4 rounded-lg shadow">
<h3 className="font-bold text-lg mb-3">Conversation</h3>
<p className="text-gray-500 italic">No parsed messages available</p>
</div>
);
}
return (
<div className="bg-white p-4 rounded-lg shadow">
<h3 className="font-bold text-lg mb-3">
Conversation ({messages.length} messages)
</h3>
<div className="space-y-3 max-h-96 overflow-y-auto">
{messages.map((message) => (
<div
key={message.id}
className={`flex ${
message.role.toLowerCase() === 'user'
? 'justify-end'
: 'justify-start'
}`}
>
<div
className={`max-w-xs lg:max-w-md px-4 py-2 rounded-lg ${
message.role.toLowerCase() === 'user'
? 'bg-blue-500 text-white'
: message.role.toLowerCase() === 'assistant'
? 'bg-gray-200 text-gray-800'
: 'bg-yellow-100 text-yellow-800'
}`}
>
<div className="flex items-center justify-between mb-1">
<span className="text-xs font-medium opacity-75 mr-2">
{message.role}
</span>
<span className="text-xs opacity-75 ml-2">
{new Date(message.timestamp).toLocaleTimeString()}
</span>
</div>
<div className="text-sm whitespace-pre-wrap">
{message.content}
</div>
</div>
</div>
))}
</div>
<div className="mt-4 pt-3 border-t text-sm text-gray-500">
<div className="flex justify-between">
<span>
First message: {new Date(messages[0].timestamp).toLocaleString()}
</span>
<span>
Last message: {new Date(messages[messages.length - 1].timestamp).toLocaleString()}
</span>
</div>
</div>
</div>
);
}

View File

@ -15,11 +15,10 @@ export default function SessionDetails({ session }: SessionDetailsProps) {
return (
<div className="bg-white p-4 rounded-lg shadow">
<h3 className="font-bold text-lg mb-3">Session Details</h3>
<div className="space-y-2">
<div className="space-y-3">
<div className="flex justify-between border-b pb-2">
<span className="text-gray-600">Session ID:</span>
<span className="font-medium">{session.sessionId || session.id}</span>
<span className="font-medium font-mono text-sm">{session.id}</span>
</div>
<div className="flex justify-between border-b pb-2">
@ -220,23 +219,19 @@ export default function SessionDetails({ session }: SessionDetailsProps) {
</div>
)}
{/* Transcript rendering is now handled by the parent page (app/dashboard/sessions/[id]/page.tsx) */}
{/* Fallback to link only if we only have the URL but no content - this might also be redundant if parent handles all transcript display */}
{(!session.transcriptContent ||
session.transcriptContent.length === 0) &&
session.fullTranscriptUrl && (
<div className="flex justify-between pt-2">
<span className="text-gray-600">Transcript:</span>
<a
href={session.fullTranscriptUrl}
target="_blank"
rel="noopener noreferrer"
className="text-blue-500 hover:text-blue-700 underline"
>
View Full Transcript
</a>
</div>
)}
{session.fullTranscriptUrl && (
<div className="flex justify-between pt-2">
<span className="text-gray-600">Transcript:</span>
<a
href={session.fullTranscriptUrl}
target="_blank"
rel="noopener noreferrer"
className="text-blue-500 hover:text-blue-700 underline"
>
View Full Transcript
</a>
</div>
)}
</div>
</div>
);