Enhance ai validation changes dialog
This commit is contained in:
@@ -8,7 +8,7 @@ import {
|
|||||||
} from "@/components/ui/dialog";
|
} from "@/components/ui/dialog";
|
||||||
import { ScrollArea } from "@/components/ui/scroll-area";
|
import { ScrollArea } from "@/components/ui/scroll-area";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Loader2, CheckIcon } from "lucide-react";
|
import { Loader2, CheckIcon, XIcon } from "lucide-react";
|
||||||
import { Code } from "@/components/ui/code";
|
import { Code } from "@/components/ui/code";
|
||||||
import {
|
import {
|
||||||
Table,
|
Table,
|
||||||
@@ -49,7 +49,12 @@ interface DebugData {
|
|||||||
promptSources?: {
|
promptSources?: {
|
||||||
systemPrompt?: { id: number; prompt_text: string };
|
systemPrompt?: { id: number; prompt_text: string };
|
||||||
generalPrompt?: { id: number; prompt_text: string };
|
generalPrompt?: { id: number; prompt_text: string };
|
||||||
companyPrompts?: Array<{ id: number; company: string; companyName?: string; prompt_text: string }>;
|
companyPrompts?: Array<{
|
||||||
|
id: number;
|
||||||
|
company: string;
|
||||||
|
companyName?: string;
|
||||||
|
prompt_text: string;
|
||||||
|
}>;
|
||||||
};
|
};
|
||||||
estimatedProcessingTime?: {
|
estimatedProcessingTime?: {
|
||||||
seconds: number | null;
|
seconds: number | null;
|
||||||
@@ -93,13 +98,69 @@ export const AiValidationDialogs: React.FC<AiValidationDialogsProps> = ({
|
|||||||
debugData,
|
debugData,
|
||||||
}) => {
|
}) => {
|
||||||
const [costPerMillionTokens, setCostPerMillionTokens] = useState(2.5); // Default cost
|
const [costPerMillionTokens, setCostPerMillionTokens] = useState(2.5); // Default cost
|
||||||
const hasCompanyPrompts = currentPrompt.debugData?.promptSources?.companyPrompts &&
|
const hasCompanyPrompts =
|
||||||
currentPrompt.debugData.promptSources.companyPrompts.length > 0;
|
currentPrompt.debugData?.promptSources?.companyPrompts &&
|
||||||
|
currentPrompt.debugData.promptSources.companyPrompts.length > 0;
|
||||||
|
|
||||||
|
// Create our own state to track changes
|
||||||
|
const [localReversionState, setLocalReversionState] = useState<
|
||||||
|
Record<string, boolean>
|
||||||
|
>({});
|
||||||
|
|
||||||
|
// Initialize local state from the isChangeReverted function when component mounts
|
||||||
|
// or when aiValidationDetails changes
|
||||||
|
React.useEffect(() => {
|
||||||
|
if (
|
||||||
|
aiValidationDetails.changeDetails &&
|
||||||
|
aiValidationDetails.changeDetails.length > 0
|
||||||
|
) {
|
||||||
|
const initialState: Record<string, boolean> = {};
|
||||||
|
|
||||||
|
aiValidationDetails.changeDetails.forEach((product) => {
|
||||||
|
product.changes.forEach((change) => {
|
||||||
|
const key = `${product.productIndex}-${change.field}`;
|
||||||
|
initialState[key] = isChangeReverted(
|
||||||
|
product.productIndex,
|
||||||
|
change.field
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
setLocalReversionState(initialState);
|
||||||
|
}
|
||||||
|
}, [aiValidationDetails.changeDetails, isChangeReverted]);
|
||||||
|
|
||||||
|
// This function will toggle the local state for a given change
|
||||||
|
const toggleChangeAcceptance = (productIndex: number, fieldKey: string) => {
|
||||||
|
const key = `${productIndex}-${fieldKey}`;
|
||||||
|
const currentlyRejected = !!localReversionState[key];
|
||||||
|
|
||||||
|
// Toggle the local state
|
||||||
|
setLocalReversionState((prev) => ({
|
||||||
|
...prev,
|
||||||
|
[key]: !prev[key],
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Only call revertAiChange when toggling to rejected state
|
||||||
|
// Since revertAiChange is specifically for rejecting changes
|
||||||
|
if (!currentlyRejected) {
|
||||||
|
revertAiChange(productIndex, fieldKey);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Function to check local reversion state
|
||||||
|
const isChangeLocallyReverted = (
|
||||||
|
productIndex: number,
|
||||||
|
fieldKey: string
|
||||||
|
): boolean => {
|
||||||
|
const key = `${productIndex}-${fieldKey}`;
|
||||||
|
return !!localReversionState[key];
|
||||||
|
};
|
||||||
|
|
||||||
// Use "full" as the default tab
|
// Use "full" as the default tab
|
||||||
const defaultTab = "full";
|
const defaultTab = "full";
|
||||||
const [activeTab, setActiveTab] = useState(defaultTab);
|
const [activeTab, setActiveTab] = useState(defaultTab);
|
||||||
|
|
||||||
// Update activeTab when the dialog is opened with new data
|
// Update activeTab when the dialog is opened with new data
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
if (currentPrompt.isOpen) {
|
if (currentPrompt.isOpen) {
|
||||||
@@ -155,7 +216,9 @@ export const AiValidationDialogs: React.FC<AiValidationDialogsProps> = ({
|
|||||||
<div className="grid grid-cols-3 gap-4 mb-4">
|
<div className="grid grid-cols-3 gap-4 mb-4">
|
||||||
<Card className="py-2">
|
<Card className="py-2">
|
||||||
<CardHeader className="py-2">
|
<CardHeader className="py-2">
|
||||||
<CardTitle className="text-base">Prompt Length</CardTitle>
|
<CardTitle className="text-base">
|
||||||
|
Prompt Length
|
||||||
|
</CardTitle>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent className="py-2">
|
<CardContent className="py-2">
|
||||||
<div className="flex flex-col space-y-2">
|
<div className="flex flex-col space-y-2">
|
||||||
@@ -163,10 +226,14 @@ export const AiValidationDialogs: React.FC<AiValidationDialogsProps> = ({
|
|||||||
<span className="text-muted-foreground">
|
<span className="text-muted-foreground">
|
||||||
Characters:
|
Characters:
|
||||||
</span>{" "}
|
</span>{" "}
|
||||||
<span className="font-semibold">{promptLength}</span>
|
<span className="font-semibold">
|
||||||
|
{promptLength}
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="text-sm">
|
<div className="text-sm">
|
||||||
<span className="text-muted-foreground">Tokens:</span>{" "}
|
<span className="text-muted-foreground">
|
||||||
|
Tokens:
|
||||||
|
</span>{" "}
|
||||||
<span className="font-semibold">
|
<span className="font-semibold">
|
||||||
~{Math.round(promptLength / 4)}
|
~{Math.round(promptLength / 4)}
|
||||||
</span>
|
</span>
|
||||||
@@ -177,7 +244,9 @@ export const AiValidationDialogs: React.FC<AiValidationDialogsProps> = ({
|
|||||||
|
|
||||||
<Card className="py-2">
|
<Card className="py-2">
|
||||||
<CardHeader className="py-2">
|
<CardHeader className="py-2">
|
||||||
<CardTitle className="text-base">Cost Estimate</CardTitle>
|
<CardTitle className="text-base">
|
||||||
|
Cost Estimate
|
||||||
|
</CardTitle>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent className="py-2">
|
<CardContent className="py-2">
|
||||||
<div className="flex flex-col space-y-2">
|
<div className="flex flex-col space-y-2">
|
||||||
@@ -225,7 +294,8 @@ export const AiValidationDialogs: React.FC<AiValidationDialogsProps> = ({
|
|||||||
<CardContent className="py-2">
|
<CardContent className="py-2">
|
||||||
<div className="flex flex-col space-y-2">
|
<div className="flex flex-col space-y-2">
|
||||||
{currentPrompt.debugData?.estimatedProcessingTime ? (
|
{currentPrompt.debugData?.estimatedProcessingTime ? (
|
||||||
currentPrompt.debugData.estimatedProcessingTime.seconds ? (
|
currentPrompt.debugData.estimatedProcessingTime
|
||||||
|
.seconds ? (
|
||||||
<>
|
<>
|
||||||
<div className="text-sm">
|
<div className="text-sm">
|
||||||
<span className="text-muted-foreground">
|
<span className="text-muted-foreground">
|
||||||
@@ -233,23 +303,28 @@ export const AiValidationDialogs: React.FC<AiValidationDialogsProps> = ({
|
|||||||
</span>{" "}
|
</span>{" "}
|
||||||
<span className="font-semibold">
|
<span className="font-semibold">
|
||||||
{formatTime(
|
{formatTime(
|
||||||
currentPrompt.debugData.estimatedProcessingTime.seconds
|
currentPrompt.debugData
|
||||||
|
.estimatedProcessingTime.seconds
|
||||||
)}
|
)}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="text-xs text-muted-foreground">
|
<div className="text-xs text-muted-foreground">
|
||||||
Based on{" "}
|
Based on{" "}
|
||||||
{currentPrompt.debugData.estimatedProcessingTime.sampleCount}{" "}
|
{
|
||||||
|
currentPrompt.debugData
|
||||||
|
.estimatedProcessingTime.sampleCount
|
||||||
|
}{" "}
|
||||||
similar validation
|
similar validation
|
||||||
{currentPrompt.debugData.estimatedProcessingTime
|
{currentPrompt.debugData
|
||||||
.sampleCount !== 1
|
.estimatedProcessingTime.sampleCount !== 1
|
||||||
? "s"
|
? "s"
|
||||||
: ""}
|
: ""}
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<div className="text-sm text-muted-foreground">
|
<div className="text-sm text-muted-foreground">
|
||||||
No historical data available for this prompt size
|
No historical data available for this prompt
|
||||||
|
size
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
) : (
|
) : (
|
||||||
@@ -278,179 +353,276 @@ export const AiValidationDialogs: React.FC<AiValidationDialogsProps> = ({
|
|||||||
{/* Prompt Sources Card - Fixed at the top of the content area */}
|
{/* Prompt Sources Card - Fixed at the top of the content area */}
|
||||||
<Card className="py-2 mb-4 flex-shrink-0">
|
<Card className="py-2 mb-4 flex-shrink-0">
|
||||||
<CardHeader className="py-2">
|
<CardHeader className="py-2">
|
||||||
<CardTitle className="text-base">Prompt Sources</CardTitle>
|
<CardTitle className="text-base">
|
||||||
|
Prompt Sources
|
||||||
|
</CardTitle>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent className="py-2">
|
<CardContent className="py-2">
|
||||||
<div className="flex flex-wrap gap-2">
|
<div className="flex flex-wrap gap-2">
|
||||||
<Badge
|
<Badge
|
||||||
variant="outline"
|
variant="outline"
|
||||||
className="bg-purple-100 hover:bg-purple-200 cursor-pointer"
|
className="bg-purple-100 hover:bg-purple-200 cursor-pointer"
|
||||||
onClick={() => document.getElementById('system-message')?.scrollIntoView({ behavior: 'smooth' })}
|
onClick={() =>
|
||||||
|
document
|
||||||
|
.getElementById("system-message")
|
||||||
|
?.scrollIntoView({ behavior: "smooth" })
|
||||||
|
}
|
||||||
>
|
>
|
||||||
System
|
System
|
||||||
</Badge>
|
</Badge>
|
||||||
<Badge
|
<Badge
|
||||||
variant="outline"
|
variant="outline"
|
||||||
className="bg-green-100 hover:bg-green-200 cursor-pointer"
|
className="bg-green-100 hover:bg-green-200 cursor-pointer"
|
||||||
onClick={() => document.getElementById('general-section')?.scrollIntoView({ behavior: 'smooth' })}
|
onClick={() =>
|
||||||
|
document
|
||||||
|
.getElementById("general-section")
|
||||||
|
?.scrollIntoView({ behavior: "smooth" })
|
||||||
|
}
|
||||||
>
|
>
|
||||||
General
|
General
|
||||||
</Badge>
|
</Badge>
|
||||||
|
|
||||||
{currentPrompt.debugData.promptSources?.companyPrompts?.map((company, idx) => (
|
{currentPrompt.debugData.promptSources?.companyPrompts?.map(
|
||||||
<Badge
|
(company, idx) => (
|
||||||
key={idx}
|
<Badge
|
||||||
variant="outline"
|
key={idx}
|
||||||
className="bg-blue-100 hover:bg-blue-200 cursor-pointer"
|
variant="outline"
|
||||||
onClick={() => document.getElementById('company-section')?.scrollIntoView({ behavior: 'smooth' })}
|
className="bg-blue-100 hover:bg-blue-200 cursor-pointer"
|
||||||
>
|
onClick={() =>
|
||||||
{company.companyName || `Company ${company.company}`}
|
document
|
||||||
</Badge>
|
.getElementById("company-section")
|
||||||
))}
|
?.scrollIntoView({ behavior: "smooth" })
|
||||||
|
}
|
||||||
<Badge
|
>
|
||||||
variant="outline"
|
{company.companyName ||
|
||||||
|
`Company ${company.company}`}
|
||||||
|
</Badge>
|
||||||
|
)
|
||||||
|
)}
|
||||||
|
|
||||||
|
<Badge
|
||||||
|
variant="outline"
|
||||||
className="bg-amber-100 hover:bg-amber-200 cursor-pointer"
|
className="bg-amber-100 hover:bg-amber-200 cursor-pointer"
|
||||||
onClick={() => document.getElementById('taxonomy-section')?.scrollIntoView({ behavior: 'smooth' })}
|
onClick={() =>
|
||||||
|
document
|
||||||
|
.getElementById("taxonomy-section")
|
||||||
|
?.scrollIntoView({ behavior: "smooth" })
|
||||||
|
}
|
||||||
>
|
>
|
||||||
Taxonomy
|
Taxonomy
|
||||||
</Badge>
|
</Badge>
|
||||||
<Badge
|
<Badge
|
||||||
variant="outline"
|
variant="outline"
|
||||||
className="bg-pink-100 hover:bg-pink-200 cursor-pointer"
|
className="bg-pink-100 hover:bg-pink-200 cursor-pointer"
|
||||||
onClick={() => document.getElementById('product-section')?.scrollIntoView({ behavior: 'smooth' })}
|
onClick={() =>
|
||||||
|
document
|
||||||
|
.getElementById("product-section")
|
||||||
|
?.scrollIntoView({ behavior: "smooth" })
|
||||||
|
}
|
||||||
>
|
>
|
||||||
Products
|
Products
|
||||||
</Badge>
|
</Badge>
|
||||||
</div>
|
</div>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
<ScrollArea className="flex-1 w-full overflow-y-auto">
|
<ScrollArea className="flex-1 w-full overflow-y-auto">
|
||||||
{currentPrompt.debugData.apiFormat.map((message, idx: number) => (
|
{currentPrompt.debugData.apiFormat.map(
|
||||||
<div key={idx} className="border rounded-md p-2 mb-4">
|
(message, idx: number) => (
|
||||||
<div
|
<div
|
||||||
id={message.role === 'system' ? 'system-message' : ''}
|
key={idx}
|
||||||
className={`p-2 mb-2 rounded-sm font-medium ${
|
className="border rounded-md p-2 mb-4"
|
||||||
message.role === 'system'
|
|
||||||
? 'bg-purple-50 text-purple-800'
|
|
||||||
: 'bg-green-50 text-green-800'
|
|
||||||
}`}
|
|
||||||
>
|
>
|
||||||
Role: {message.role}
|
<div
|
||||||
|
id={
|
||||||
|
message.role === "system"
|
||||||
|
? "system-message"
|
||||||
|
: ""
|
||||||
|
}
|
||||||
|
className={`p-2 mb-2 rounded-sm font-medium ${
|
||||||
|
message.role === "system"
|
||||||
|
? "bg-purple-50 text-purple-800"
|
||||||
|
: "bg-green-50 text-green-800"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
Role: {message.role}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Code
|
||||||
|
className={`whitespace-pre-wrap p-4 break-normal max-w-full ${
|
||||||
|
message.role === "system"
|
||||||
|
? "bg-purple-50/30"
|
||||||
|
: "bg-green-50/30"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{message.role === "user" ? (
|
||||||
|
<div className="text-wrapper">
|
||||||
|
{(() => {
|
||||||
|
const content = message.content;
|
||||||
|
|
||||||
|
// Find section boundaries by looking for specific markers
|
||||||
|
const companySpecificStartIndex =
|
||||||
|
content.indexOf(
|
||||||
|
"--- COMPANY-SPECIFIC INSTRUCTIONS ---"
|
||||||
|
);
|
||||||
|
const companySpecificEndIndex =
|
||||||
|
content.indexOf(
|
||||||
|
"--- END COMPANY-SPECIFIC INSTRUCTIONS ---"
|
||||||
|
);
|
||||||
|
|
||||||
|
const taxonomyStartIndex =
|
||||||
|
content.indexOf(
|
||||||
|
"All Available Categories:"
|
||||||
|
);
|
||||||
|
const taxonomyFallbackStartIndex =
|
||||||
|
content.indexOf(
|
||||||
|
"Available Categories:"
|
||||||
|
);
|
||||||
|
const actualTaxonomyStartIndex =
|
||||||
|
taxonomyStartIndex >= 0
|
||||||
|
? taxonomyStartIndex
|
||||||
|
: taxonomyFallbackStartIndex;
|
||||||
|
|
||||||
|
const productDataStartIndex =
|
||||||
|
content.indexOf(
|
||||||
|
"----------Here is the product data to validate----------"
|
||||||
|
);
|
||||||
|
|
||||||
|
// If we can't find any markers, just return the content as-is
|
||||||
|
if (
|
||||||
|
actualTaxonomyStartIndex < 0 &&
|
||||||
|
productDataStartIndex < 0 &&
|
||||||
|
companySpecificStartIndex < 0
|
||||||
|
) {
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine section indices
|
||||||
|
let generalEndIndex = content.length;
|
||||||
|
|
||||||
|
if (companySpecificStartIndex >= 0) {
|
||||||
|
generalEndIndex =
|
||||||
|
companySpecificStartIndex;
|
||||||
|
} else if (
|
||||||
|
actualTaxonomyStartIndex >= 0
|
||||||
|
) {
|
||||||
|
generalEndIndex =
|
||||||
|
actualTaxonomyStartIndex;
|
||||||
|
} else if (productDataStartIndex >= 0) {
|
||||||
|
generalEndIndex = productDataStartIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine where taxonomy starts
|
||||||
|
let taxonomyEndIndex = content.length;
|
||||||
|
if (productDataStartIndex >= 0) {
|
||||||
|
taxonomyEndIndex =
|
||||||
|
productDataStartIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Segments to render with appropriate styling
|
||||||
|
const segments = [];
|
||||||
|
|
||||||
|
// General section (beginning to company/taxonomy/product)
|
||||||
|
if (generalEndIndex > 0) {
|
||||||
|
segments.push(
|
||||||
|
<div
|
||||||
|
id="general-section"
|
||||||
|
key="general"
|
||||||
|
className="border-l-4 border-green-500 pl-4 py-0 my-1"
|
||||||
|
>
|
||||||
|
<div className="text-xs font-semibold text-green-700 mb-2">
|
||||||
|
General Prompt
|
||||||
|
</div>
|
||||||
|
<pre className="whitespace-pre-wrap">
|
||||||
|
{content.substring(
|
||||||
|
0,
|
||||||
|
generalEndIndex
|
||||||
|
)}
|
||||||
|
</pre>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Company-specific section if present
|
||||||
|
if (
|
||||||
|
companySpecificStartIndex >= 0 &&
|
||||||
|
companySpecificEndIndex >= 0
|
||||||
|
) {
|
||||||
|
segments.push(
|
||||||
|
<div
|
||||||
|
id="company-section"
|
||||||
|
key="company"
|
||||||
|
className="border-l-4 border-blue-500 pl-4 py-0 my-1"
|
||||||
|
>
|
||||||
|
<div className="text-xs font-semibold text-blue-700 mb-2">
|
||||||
|
Company-Specific Instructions
|
||||||
|
</div>
|
||||||
|
<pre className="whitespace-pre-wrap">
|
||||||
|
{content.substring(
|
||||||
|
companySpecificStartIndex,
|
||||||
|
companySpecificEndIndex +
|
||||||
|
"--- END COMPANY-SPECIFIC INSTRUCTIONS ---"
|
||||||
|
.length
|
||||||
|
)}
|
||||||
|
</pre>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Taxonomy section
|
||||||
|
if (actualTaxonomyStartIndex >= 0) {
|
||||||
|
const taxEnd = taxonomyEndIndex;
|
||||||
|
segments.push(
|
||||||
|
<div
|
||||||
|
id="taxonomy-section"
|
||||||
|
key="taxonomy"
|
||||||
|
className="border-l-4 border-amber-500 pl-4 py-0 my-1"
|
||||||
|
>
|
||||||
|
<div className="text-xs font-semibold text-amber-700 mb-2">
|
||||||
|
Taxonomy Data
|
||||||
|
</div>
|
||||||
|
<pre className="whitespace-pre-wrap">
|
||||||
|
{content.substring(
|
||||||
|
actualTaxonomyStartIndex,
|
||||||
|
taxEnd
|
||||||
|
)}
|
||||||
|
</pre>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Product data section
|
||||||
|
if (productDataStartIndex >= 0) {
|
||||||
|
segments.push(
|
||||||
|
<div
|
||||||
|
id="product-section"
|
||||||
|
key="product"
|
||||||
|
className="border-l-4 border-pink-500 pl-4 py-0 my-1"
|
||||||
|
>
|
||||||
|
<div className="text-xs font-semibold text-pink-700 mb-2">
|
||||||
|
Product Data
|
||||||
|
</div>
|
||||||
|
<pre className="whitespace-pre-wrap">
|
||||||
|
{content.substring(
|
||||||
|
productDataStartIndex
|
||||||
|
)}
|
||||||
|
</pre>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return <>{segments}</>;
|
||||||
|
})()}
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<pre className="whitespace-pre-wrap">
|
||||||
|
{message.content}
|
||||||
|
</pre>
|
||||||
|
)}
|
||||||
|
</Code>
|
||||||
</div>
|
</div>
|
||||||
|
)
|
||||||
<Code className={`whitespace-pre-wrap p-4 break-normal max-w-full ${
|
)}
|
||||||
message.role === 'system'
|
|
||||||
? 'bg-purple-50/30'
|
|
||||||
: 'bg-green-50/30'
|
|
||||||
}`}>
|
|
||||||
{message.role === 'user' ? (
|
|
||||||
<div className="text-wrapper">
|
|
||||||
{(() => {
|
|
||||||
const content = message.content;
|
|
||||||
|
|
||||||
// Find section boundaries by looking for specific markers
|
|
||||||
const companySpecificStartIndex = content.indexOf('--- COMPANY-SPECIFIC INSTRUCTIONS ---');
|
|
||||||
const companySpecificEndIndex = content.indexOf('--- END COMPANY-SPECIFIC INSTRUCTIONS ---');
|
|
||||||
|
|
||||||
const taxonomyStartIndex = content.indexOf('All Available Categories:');
|
|
||||||
const taxonomyFallbackStartIndex = content.indexOf('Available Categories:');
|
|
||||||
const actualTaxonomyStartIndex = taxonomyStartIndex >= 0 ? taxonomyStartIndex : taxonomyFallbackStartIndex;
|
|
||||||
|
|
||||||
const productDataStartIndex = content.indexOf('----------Here is the product data to validate----------');
|
|
||||||
|
|
||||||
// If we can't find any markers, just return the content as-is
|
|
||||||
if (actualTaxonomyStartIndex < 0 && productDataStartIndex < 0 && companySpecificStartIndex < 0) {
|
|
||||||
return content;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Determine section indices
|
|
||||||
let generalEndIndex = content.length;
|
|
||||||
|
|
||||||
if (companySpecificStartIndex >= 0) {
|
|
||||||
generalEndIndex = companySpecificStartIndex;
|
|
||||||
} else if (actualTaxonomyStartIndex >= 0) {
|
|
||||||
generalEndIndex = actualTaxonomyStartIndex;
|
|
||||||
} else if (productDataStartIndex >= 0) {
|
|
||||||
generalEndIndex = productDataStartIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Determine where taxonomy starts
|
|
||||||
let taxonomyEndIndex = content.length;
|
|
||||||
if (productDataStartIndex >= 0) {
|
|
||||||
taxonomyEndIndex = productDataStartIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Segments to render with appropriate styling
|
|
||||||
const segments = [];
|
|
||||||
|
|
||||||
// General section (beginning to company/taxonomy/product)
|
|
||||||
if (generalEndIndex > 0) {
|
|
||||||
segments.push(
|
|
||||||
<div id="general-section" key="general" className="border-l-4 border-green-500 pl-4 py-0 my-1">
|
|
||||||
<div className="text-xs font-semibold text-green-700 mb-2">
|
|
||||||
General Prompt
|
|
||||||
</div>
|
|
||||||
<pre className="whitespace-pre-wrap">
|
|
||||||
{content.substring(0, generalEndIndex)}
|
|
||||||
</pre>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Company-specific section if present
|
|
||||||
if (companySpecificStartIndex >= 0 && companySpecificEndIndex >= 0) {
|
|
||||||
segments.push(
|
|
||||||
<div id="company-section" key="company" className="border-l-4 border-blue-500 pl-4 py-0 my-1">
|
|
||||||
<div className="text-xs font-semibold text-blue-700 mb-2">
|
|
||||||
Company-Specific Instructions
|
|
||||||
</div>
|
|
||||||
<pre className="whitespace-pre-wrap">
|
|
||||||
{content.substring(companySpecificStartIndex, companySpecificEndIndex + '--- END COMPANY-SPECIFIC INSTRUCTIONS ---'.length)}
|
|
||||||
</pre>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Taxonomy section
|
|
||||||
if (actualTaxonomyStartIndex >= 0) {
|
|
||||||
const taxEnd = taxonomyEndIndex;
|
|
||||||
segments.push(
|
|
||||||
<div id="taxonomy-section" key="taxonomy" className="border-l-4 border-amber-500 pl-4 py-0 my-1">
|
|
||||||
<div className="text-xs font-semibold text-amber-700 mb-2">
|
|
||||||
Taxonomy Data
|
|
||||||
</div>
|
|
||||||
<pre className="whitespace-pre-wrap">
|
|
||||||
{content.substring(actualTaxonomyStartIndex, taxEnd)}
|
|
||||||
</pre>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Product data section
|
|
||||||
if (productDataStartIndex >= 0) {
|
|
||||||
segments.push(
|
|
||||||
<div id="product-section" key="product" className="border-l-4 border-pink-500 pl-4 py-0 my-1">
|
|
||||||
<div className="text-xs font-semibold text-pink-700 mb-2">
|
|
||||||
Product Data
|
|
||||||
</div>
|
|
||||||
<pre className="whitespace-pre-wrap">
|
|
||||||
{content.substring(productDataStartIndex)}
|
|
||||||
</pre>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return <>{segments}</>;
|
|
||||||
})()}
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<pre className="whitespace-pre-wrap">{message.content}</pre>
|
|
||||||
)}
|
|
||||||
</Code>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</ScrollArea>
|
</ScrollArea>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
@@ -566,14 +738,14 @@ export const AiValidationDialogs: React.FC<AiValidationDialogsProps> = ({
|
|||||||
setAiValidationDetails((prev) => ({ ...prev, isOpen: open }))
|
setAiValidationDetails((prev) => ({ ...prev, isOpen: open }))
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<DialogContent className="max-w-4xl">
|
<DialogContent className="max-w-6xl w-[90vw]">
|
||||||
<DialogHeader>
|
<DialogHeader>
|
||||||
<DialogTitle>AI Validation Results</DialogTitle>
|
<DialogTitle>AI Validation Results</DialogTitle>
|
||||||
<DialogDescription>
|
<DialogDescription>
|
||||||
Review the changes and warnings suggested by the AI
|
Review the changes and warnings suggested by the AI
|
||||||
</DialogDescription>
|
</DialogDescription>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
<ScrollArea className="max-h-[60vh]">
|
<ScrollArea className="max-h-[70vh]">
|
||||||
{aiValidationDetails.changeDetails &&
|
{aiValidationDetails.changeDetails &&
|
||||||
aiValidationDetails.changeDetails.length > 0 ? (
|
aiValidationDetails.changeDetails.length > 0 ? (
|
||||||
<div className="mb-6 space-y-6">
|
<div className="mb-6 space-y-6">
|
||||||
@@ -595,10 +767,16 @@ export const AiValidationDialogs: React.FC<AiValidationDialogsProps> = ({
|
|||||||
<Table>
|
<Table>
|
||||||
<TableHeader>
|
<TableHeader>
|
||||||
<TableRow>
|
<TableRow>
|
||||||
<TableHead className="w-[180px]">Field</TableHead>
|
<TableHead className="">Field</TableHead>
|
||||||
<TableHead>Original Value</TableHead>
|
<TableHead className="w-[35%]">
|
||||||
<TableHead>Corrected Value</TableHead>
|
Original Value
|
||||||
<TableHead className="text-right">Action</TableHead>
|
</TableHead>
|
||||||
|
<TableHead className="w-[35%]">
|
||||||
|
Corrected Value
|
||||||
|
</TableHead>
|
||||||
|
<TableHead className="text-right">
|
||||||
|
Accept Changes?
|
||||||
|
</TableHead>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
</TableHeader>
|
</TableHeader>
|
||||||
<TableBody>
|
<TableBody>
|
||||||
@@ -609,7 +787,7 @@ export const AiValidationDialogs: React.FC<AiValidationDialogsProps> = ({
|
|||||||
const fieldLabel = field
|
const fieldLabel = field
|
||||||
? field.label
|
? field.label
|
||||||
: change.field;
|
: change.field;
|
||||||
const isReverted = isChangeReverted(
|
const isReverted = isChangeLocallyReverted(
|
||||||
product.productIndex,
|
product.productIndex,
|
||||||
change.field
|
change.field
|
||||||
);
|
);
|
||||||
@@ -632,7 +810,6 @@ export const AiValidationDialogs: React.FC<AiValidationDialogsProps> = ({
|
|||||||
dangerouslySetInnerHTML={{
|
dangerouslySetInnerHTML={{
|
||||||
__html: originalHtml,
|
__html: originalHtml,
|
||||||
}}
|
}}
|
||||||
className={isReverted ? "font-medium" : ""}
|
|
||||||
/>
|
/>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell>
|
<TableCell>
|
||||||
@@ -640,36 +817,46 @@ export const AiValidationDialogs: React.FC<AiValidationDialogsProps> = ({
|
|||||||
dangerouslySetInnerHTML={{
|
dangerouslySetInnerHTML={{
|
||||||
__html: correctedHtml,
|
__html: correctedHtml,
|
||||||
}}
|
}}
|
||||||
className={!isReverted ? "font-medium" : ""}
|
|
||||||
/>
|
/>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell className="text-right">
|
<TableCell className="text-right align-top">
|
||||||
<div className="mt-2">
|
<div className="flex justify-end gap-2">
|
||||||
{isReverted ? (
|
<Button
|
||||||
<Button
|
variant="outline"
|
||||||
variant="ghost"
|
size="sm"
|
||||||
size="sm"
|
onClick={() => {
|
||||||
className="text-green-600 bg-green-50 hover:bg-green-100 hover:text-green-700"
|
// Toggle to Accepted state if currently rejected
|
||||||
disabled
|
toggleChangeAcceptance(
|
||||||
>
|
product.productIndex,
|
||||||
<CheckIcon className="w-4 h-4 mr-1" />
|
change.field
|
||||||
Reverted
|
);
|
||||||
</Button>
|
}}
|
||||||
) : (
|
className={
|
||||||
<Button
|
!isReverted
|
||||||
variant="outline"
|
? "bg-green-100 text-green-600 border-green-300 flex items-center"
|
||||||
size="sm"
|
: "border-gray-200 text-gray-600 hover:bg-green-50 hover:text-green-600 hover:border-green-200 flex items-center"
|
||||||
onClick={() => {
|
}
|
||||||
// Call the revert function directly
|
>
|
||||||
revertAiChange(
|
<CheckIcon className="h-4 w-4" />
|
||||||
product.productIndex,
|
</Button>
|
||||||
change.field
|
<Button
|
||||||
);
|
variant="outline"
|
||||||
}}
|
size="sm"
|
||||||
>
|
onClick={() => {
|
||||||
Revert Change
|
// Toggle to Rejected state if currently accepted
|
||||||
</Button>
|
toggleChangeAcceptance(
|
||||||
)}
|
product.productIndex,
|
||||||
|
change.field
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
className={
|
||||||
|
isReverted
|
||||||
|
? "bg-red-100 text-red-600 border-red-300 flex items-center"
|
||||||
|
: "border-gray-200 text-gray-600 hover:bg-red-50 hover:text-red-600 hover:border-red-200 flex items-center"
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<XIcon className="h-4 w-4" />
|
||||||
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
|
|||||||
Reference in New Issue
Block a user