"use client"; import { useEffect } from "react"; import { useWorkflowStore } from "@/store/workflowStore"; import { PredictedCostResult, CostBreakdownItem, formatCost } from "@/utils/costCalculator"; import { ProviderType } from "@/types/providers"; interface CostDialogProps { predictedCost: PredictedCostResult; incurredCost: number; onClose: () => void; } /** * Provider icon component - colored dot with provider indicator */ function ProviderIcon({ provider }: { provider: ProviderType }) { const colors: Record = { gemini: { bg: "bg-green-500/20", text: "text-green-300" }, fal: { bg: "bg-purple-500/20", text: "text-purple-300" }, replicate: { bg: "bg-blue-500/20", text: "text-blue-300" }, openai: { bg: "bg-teal-500/20", text: "text-teal-300" }, }; const labels: Record = { gemini: "G", fal: "f", replicate: "R", openai: "O", }; const color = colors[provider] || colors.gemini; return ( {labels[provider]} ); } /** * Get display name for provider */ function getProviderDisplayName(provider: ProviderType): string { const names: Record = { gemini: "Gemini", fal: "fal.ai", replicate: "Replicate", openai: "OpenAI", }; return names[provider] || provider; } /** * Get model page URL for external providers */ function getModelUrl(provider: ProviderType, modelId: string): string | null { if (provider === "replicate") { // modelId format: "owner/model" or "owner/model:version" const baseModelId = modelId.split(":")[0]; return `https://replicate.com/${baseModelId}`; } if (provider === "fal") { // modelId format: "fal-ai/flux/dev" or similar return `https://fal.ai/models/${modelId}`; } return null; } /** * External link icon component */ function ExternalLinkIcon() { return ( ); } export function CostDialog({ predictedCost, incurredCost, onClose }: CostDialogProps) { const resetIncurredCost = useWorkflowStore((state) => state.resetIncurredCost); useEffect(() => { const handleKeyDown = (e: KeyboardEvent) => { if (e.key === "Escape") { onClose(); } }; window.addEventListener("keydown", handleKeyDown); return () => window.removeEventListener("keydown", handleKeyDown); }, [onClose]); const handleReset = () => { if (confirm("Reset incurred cost to $0.00?")) { resetIncurredCost(); } }; // Separate Gemini (reliable pricing) from external providers (unreliable pricing) const geminiItems = predictedCost.breakdown.filter((item) => item.provider === "gemini"); const externalItems = predictedCost.breakdown.filter( (item) => item.provider === "fal" || item.provider === "replicate" ); // Group external items by provider const externalByProvider = new Map(); externalItems.forEach((item) => { const existing = externalByProvider.get(item.provider); if (existing) { existing.push(item); } else { externalByProvider.set(item.provider, [item]); } }); const geminiTotal = geminiItems.reduce((sum, item) => sum + (item.subtotal ?? 0), 0); const externalNodeCount = externalItems.reduce((sum, item) => sum + item.count, 0); const hasGemini = geminiItems.length > 0; const hasExternal = externalItems.length > 0; return (

Workflow Costs

{/* Gemini Cost Section - prices are reliable */} {hasGemini && (
Gemini Cost {formatCost(geminiTotal)}
{geminiItems.map((item, idx) => (
{item.count}x {item.modelName} {item.subtotal !== null ? formatCost(item.subtotal) : "—"}
))}
)} {/* External Providers Section - show model links instead of prices */} {hasExternal && (
External Providers {externalNodeCount} node{externalNodeCount !== 1 ? "s" : ""}
{Array.from(externalByProvider.entries()).map(([provider, items]) => (
{getProviderDisplayName(provider)}
{items.map((item, idx) => { const modelUrl = getModelUrl(provider, item.modelId); return (
{item.count}x {item.modelName} {modelUrl && ( View model )}
); })}
))}

Pricing varies by model, hardware, and usage. Check provider for details.

)} {/* No nodes message */} {predictedCost.nodeCount === 0 && (

No generation nodes in workflow

)} {/* Incurred Cost Section - Gemini only */}
Incurred Cost {formatCost(incurredCost)}

Actual API spend from Gemini generations

{incurredCost > 0 && ( )}
{/* Pricing Note */}

Gemini pricing: $0.039-$0.24/image. External providers not tracked.

); }