"use client";
import { useMemo } from "react";
import { clsx } from "clsx";
interface AssistantInsightMetric {
label: string;
value: string;
tone?: "neutral" | "good" | "warn" | "danger" | "info";
}
interface AssistantInsightSection {
title: string;
metrics: AssistantInsightMetric[];
}
interface AssistantInsight {
kind: "chargeability" | "resource_match" | "holiday_region" | "resource_holidays";
title: string;
subtitle?: string;
metrics: AssistantInsightMetric[];
sections?: AssistantInsightSection[];
}
interface ChatMessageProps {
role: "user" | "assistant";
content: string;
insights?: AssistantInsight[];
}
function renderMarkdown(text: string) {
const lines = text.split("\n");
const elements: React.ReactNode[] = [];
let listItems: React.ReactNode[] = [];
let listType: "ul" | "ol" | null = null;
const flushList = () => {
if (listItems.length > 0 && listType) {
const Tag = listType;
elements.push(
{inlineFormat(line)}
); } flushList(); return elements; } function inlineFormat(text: string): React.ReactNode { const parts: React.ReactNode[] = []; const regex = /(\*\*(.+?)\*\*|\*(.+?)\*|`(.+?)`)/g; let lastIndex = 0; let match: RegExpExecArray | null; while ((match = regex.exec(text)) !== null) { if (match.index > lastIndex) { parts.push(text.slice(lastIndex, match.index)); } if (match[2]) { parts.push({match[2]}); } else if (match[3]) { parts.push({match[3]}); } else if (match[4]) { parts.push(
{match[4]}
,
);
}
lastIndex = match.index + match[0].length;
}
if (lastIndex < text.length) {
parts.push(text.slice(lastIndex));
}
return parts.length === 1 ? parts[0] : <>{parts}>;
}
function metricToneClasses(tone: AssistantInsightMetric["tone"] | undefined): string {
switch (tone) {
case "good":
return "border-emerald-200 bg-emerald-50 text-emerald-700 dark:border-emerald-900/60 dark:bg-emerald-950/30 dark:text-emerald-300";
case "warn":
return "border-amber-200 bg-amber-50 text-amber-700 dark:border-amber-900/60 dark:bg-amber-950/30 dark:text-amber-300";
case "danger":
return "border-red-200 bg-red-50 text-red-700 dark:border-red-900/60 dark:bg-red-950/30 dark:text-red-300";
case "info":
return "border-sky-200 bg-sky-50 text-sky-700 dark:border-sky-900/60 dark:bg-sky-950/30 dark:text-sky-300";
default:
return "border-gray-200 bg-white text-gray-700 dark:border-slate-700 dark:bg-slate-900/60 dark:text-gray-200";
}
}
function InsightMetric({ metric }: { metric: AssistantInsightMetric }) {
return (