import { useState, useEffect } from "react"; import { useTranslation } from "react-i18next"; import { Layout } from "@/components/layout"; import { Card, CardContent } from "@/components/ui/card"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { Skeleton } from "@/components/ui/skeleton"; import { Progress } from "@/components/ui/progress"; import { Star, AlertTriangle, DollarSign, ThumbsUp, RotateCcw } from "lucide-react"; import { Link } from "wouter"; import { customFetch } from "@workspace/api-client-react"; import { useToast } from "@/hooks/use-toast"; export default function RedundancyPage() { const { t } = useTranslation(); const [data, setData] = useState(null); const [loading, setLoading] = useState(true); const { toast } = useToast(); useEffect(() => { customFetch("/api/admin/redundancy") .then(setData) .catch(() => {}) .finally(() => setLoading(false)); }, []); async function handleEvaluate(toolId: number, relatedToolId: number, betterToolId: number) { try { await customFetch("/api/admin/redundancy/evaluate", { method: "POST", body: JSON.stringify({ toolId, relatedToolId, betterToolId }), }); toast({ title: t("redundancy.toastEvalSaved") }); setData(await customFetch("/api/admin/redundancy")); } catch { toast({ title: t("redundancy.toastEvalFailed"), variant: "destructive" }); } } function certaintyColor(c: string) { return c === "high" ? "bg-green-100 text-green-800 border-green-300" : c === "medium" ? "bg-amber-100 text-amber-800 border-amber-300" : "bg-gray-100 text-gray-600 border-gray-300"; } return (

{t("redundancy.title")}

{t("redundancy.subtitle")}

{loading ? (
{Array.from({ length: 3 }).map((_, i) => )}
) : data && data.length > 0 ? (
{data.map((group) => (

{group.category}

{t("redundancy.toolsComparisons", { tools: group.tools.length, pairs: group.pairs.length })}

{group.totalMonthlyCost > 0 && ( {group.totalMonthlyCost.toFixed(2)}{t("redundancy.totalMonthly")} )}
{group.tools.map((tool: any) => (
{tool.name} {tool.costs?.length > 0 && tool.totalMonthly > 0 && ( {tool.totalMonthly.toFixed(2)}{t("redundancy.perMonth")} )}
{tool.ratingCount} {t("redundancy.reviews")} {tool.avgCombined != null && ( <> · {tool.avgCombined.toFixed(1)} )}
{tool.costs?.map((c: any, i: number) => ( {c.licenseType}{c.billingPeriod ? ` (${c.billingPeriod})` : ""} ))}
{tool.features.length} {t("redundancy.features")}
))}
{group.pairs.length > 0 && (

{t("redundancy.comparisonsTitle")}

{group.pairs.map((pair: any, i: number) => (
{pair.a.name} {pair.a.avgCombined != null ? pair.a.avgCombined.toFixed(1) : "N/A"} ★ {pair.a.totalMonthly > 0 ? ` · ${pair.a.totalMonthly.toFixed(2)}${t("redundancy.perMonth")}` : ""}
{t("redundancy.vs")}
{pair.overlap}%
{pair.b.name} {pair.b.avgCombined != null ? pair.b.avgCombined.toFixed(1) : "N/A"} ★ {pair.b.totalMonthly > 0 ? ` · ${pair.b.totalMonthly.toFixed(2)}${t("redundancy.perMonth")}` : ""}
{pair.recommendation.reason && ( {pair.recommendation.betterName} )}
{pair.recommendation.reason && (

{pair.recommendation.reason}

)}
))}
)}
))}
) : (

{t("redundancy.noTools")}

)}
); }