3import { Icon } from '@/contexts/IconContext';
4import { useEffect, useState } from 'react';
19export default function DuplicateDescriptionsReport() {
20 const [duplicates, setDuplicates] = useState<Duplicate[]>([]);
21 const [loading, setLoading] = useState(true);
22 const [error, setError] = useState<string | null>(null);
25 async function fetchDuplicates() {
30 const response = await fetch('/api/v1/reports/advisor/duplicate-descriptions');
31 const result = await response.json();
33 if (!response.ok || !result.success) {
34 throw new Error(result.error || 'Failed to fetch duplicate descriptions');
37 setDuplicates(result.data.duplicates || []);
39 console.error('Error fetching duplicates:', err);
40 setError(err.message || 'Failed to load duplicate descriptions');
50 <div className="p-6 min-h-screen bg-bg">
51 <div className="mb-6">
52 <div className="flex items-center gap-3 mb-2">
53 <a href="/pages/reports?source=advisor" className="text-muted hover:text-brand">
54 <Icon name="arrow_back" size={24} />
56 <h1 className="text-3xl font-bold text-text">Duplicate Product Descriptions</h1>
58 <p className="text-muted ml-9">
59 Products with identical or very similar descriptions
64 <div className="bg-surface rounded-lg shadow-sm border border-border p-12 text-center">
65 <div className="inline-block animate-spin rounded-full h-8 w-8 border-b-2 border-brand mb-4"></div>
66 <p className="text-muted">Loading duplicate descriptions...</p>
69 <div className="bg-surface rounded-lg shadow-sm border border-border p-8">
70 <div className="flex items-start gap-3">
71 <Icon name="error" size={24} className="text-error flex-shrink-0 mt-1" />
73 <h3 className="text-lg font-semibold text-text mb-1">Error Loading Data</h3>
74 <p className="text-muted">{error}</p>
79 <div className="bg-surface rounded-lg shadow-sm border border-border overflow-hidden">
80 <div className="overflow-x-auto">
81 <table className="w-full">
82 <thead className="bg-[var(--brand)] text-surface">
84 <th className="px-6 py-3 text-left text-xs font-medium uppercase tracking-wider">
87 <th className="px-6 py-3 text-left text-xs font-medium uppercase tracking-wider">
90 <th className="px-6 py-3 text-center text-xs font-medium uppercase tracking-wider">
93 <th className="px-6 py-3 text-left text-xs font-medium uppercase tracking-wider">
98 <tbody className="divide-y divide-border">
99 {duplicates.length === 0 ? (
101 <td colSpan={4} className="px-6 py-12 text-center">
102 <Icon name="check_circle" size={48} className="text-success mx-auto mb-3" />
103 <p className="text-muted">No duplicate descriptions found</p>
107 duplicates.map((dup, idx) => (
108 <tr key={idx} className="hover:bg-surface-2">
109 <td className="px-6 py-4 text-sm font-medium text-text">
112 <td className="px-6 py-4 text-sm text-muted">
113 <div className="space-y-1">
114 {dup.products.map((product, pIdx) => (
115 <div key={pIdx} className="flex items-center gap-2">
117 href={`/pages/products/${product.id}`}
118 className="text-brand hover:text-brand2 hover:underline"
122 <span className="text-xs text-muted">(ID: {product.id})</span>
124 <span className="text-xs px-1.5 py-0.5 rounded bg-error/20 text-error" title="Cannot be sold">NoSell</span>
127 <span className="text-xs px-1.5 py-0.5 rounded bg-warning/20 text-warning" title="Hidden from display">Hidden</span>
133 <td className="px-6 py-4 whitespace-nowrap text-center">
134 <span className={`px-2 py-1 text-xs rounded ${
135 dup.count >= 3 ? 'bg-error/20 text-error' : 'bg-warning/20 text-warning'
140 <td className="px-6 py-4 text-sm">
141 <div className="flex flex-wrap gap-2">
142 {dup.products.map((product, pIdx) => (
145 href={`/pages/products/${product.id}`}
146 className="text-brand hover:text-brand2 hover:underline"