mirror of
https://github.com/christianvidalwolf-prog/Craze-Data-check.git
synced 2026-08-03 14:55:24 +02:00
feat: include article details (DE/EN) in completeness check and make them editable
This commit is contained in:
@@ -21,6 +21,8 @@ export function DataCompleteness({ data, headers }: DataCompletenessProps) {
|
||||
COLUMNS.TARIFF_CODE,
|
||||
COLUMNS.COUNTRY_ORIGIN,
|
||||
COLUMNS.RECOMMENDED_AGE,
|
||||
COLUMNS.DETAILS_DE,
|
||||
COLUMNS.DETAILS_EN,
|
||||
COLUMNS.LONG_DE,
|
||||
COLUMNS.LONG_EN,
|
||||
COLUMNS.SHORT_DE,
|
||||
@@ -70,7 +72,7 @@ export function DataCompleteness({ data, headers }: DataCompletenessProps) {
|
||||
<div className="flex flex-col h-full bg-slate-800 rounded-xl border border-slate-700 shadow-xl overflow-hidden">
|
||||
<div className="p-4 border-b border-slate-700 bg-slate-800/50">
|
||||
<h2 className="text-lg font-semibold text-white">Data Completeness</h2>
|
||||
<p className="text-sm text-slate-400">Evaluating 10 key fields per product.</p>
|
||||
<p className="text-sm text-slate-400">Evaluating 12 key fields per product.</p>
|
||||
</div>
|
||||
|
||||
<div className="overflow-x-auto flex-1">
|
||||
|
||||
@@ -19,6 +19,8 @@ export function EditPanel({ row, rowIndex, onSave, onClose, onCaptureState }: Ed
|
||||
longEn: row[COLUMNS.LONG_EN] || '',
|
||||
shortDe: row[COLUMNS.SHORT_DE] || '',
|
||||
shortEn: row[COLUMNS.SHORT_EN] || '',
|
||||
detailsDe: row[COLUMNS.DETAILS_DE] || '',
|
||||
detailsEn: row[COLUMNS.DETAILS_EN] || '',
|
||||
innerW: row[COLUMNS.INNER_W] || '',
|
||||
innerL: row[COLUMNS.INNER_L] || '',
|
||||
innerH: row[COLUMNS.INNER_H] || '',
|
||||
@@ -48,8 +50,12 @@ export function EditPanel({ row, rowIndex, onSave, onClose, onCaptureState }: Ed
|
||||
outerH: COLUMNS.OUTER_H,
|
||||
unitsOuter: COLUMNS.UNITS_OUTER,
|
||||
moq: COLUMNS.MOQ,
|
||||
detailsDe: COLUMNS.DETAILS_DE,
|
||||
detailsEn: COLUMNS.DETAILS_EN,
|
||||
};
|
||||
return formData[field] !== (row[colMap[field]] || '');
|
||||
const colIndex = (colMap as Record<string, number>)[field as string];
|
||||
if (colIndex === undefined) return false;
|
||||
return formData[field] !== (row[colIndex] || '');
|
||||
};
|
||||
|
||||
const handleGenerate = async (field: keyof typeof formData) => {
|
||||
@@ -65,7 +71,7 @@ export function EditPanel({ row, rowIndex, onSave, onClose, onCaptureState }: Ed
|
||||
|
||||
try {
|
||||
let prompt = '';
|
||||
const baseContext = `Article Name: ${row[COLUMNS.ARTICLE_NAME]}\nArticle Details (EN): ${row[COLUMNS.DETAILS_EN] || 'N/A'}\nArticle Details (DE): ${row[COLUMNS.DETAILS_DE] || 'N/A'}`;
|
||||
const baseContext = `Article Name: ${row[COLUMNS.ARTICLE_NAME]}\nArticle Details (EN): ${formData.detailsEn || 'N/A'}\nArticle Details (DE): ${formData.detailsDe || 'N/A'}`;
|
||||
|
||||
const systemPrompt = "You are a professional copywriter and expert translator for CRAZE GmbH, a German toy company. You excel at translating product descriptions between German and English, maintaining the commercial and professional tone while ensuring all technical toy details are accurate. Use clear, engaging language.";
|
||||
|
||||
@@ -143,6 +149,8 @@ export function EditPanel({ row, rowIndex, onSave, onClose, onCaptureState }: Ed
|
||||
newRow[COLUMNS.OUTER_H] = formData.outerH;
|
||||
newRow[COLUMNS.UNITS_OUTER] = formData.unitsOuter;
|
||||
newRow[COLUMNS.MOQ] = formData.moq;
|
||||
newRow[COLUMNS.DETAILS_DE] = formData.detailsDe;
|
||||
newRow[COLUMNS.DETAILS_EN] = formData.detailsEn;
|
||||
onSave(rowIndex, newRow);
|
||||
};
|
||||
|
||||
@@ -230,11 +238,27 @@ export function EditPanel({ row, rowIndex, onSave, onClose, onCaptureState }: Ed
|
||||
</div>
|
||||
<div className="col-span-2">
|
||||
<span className="block text-xs text-slate-500 mb-1">Details (DE)</span>
|
||||
<p className="text-sm text-slate-300 line-clamp-2" title={row[COLUMNS.DETAILS_DE]}>{row[COLUMNS.DETAILS_DE] || '-'}</p>
|
||||
<textarea
|
||||
value={formData.detailsDe}
|
||||
onChange={e => setFormData(prev => ({ ...prev, detailsDe: e.target.value }))}
|
||||
className={cn(
|
||||
"w-full h-20 bg-slate-900 border rounded p-2 text-sm text-white focus:outline-none focus:ring-1 transition-colors resize-none",
|
||||
isModified('detailsDe') ? "border-blue-500 focus:ring-blue-500" : "border-slate-700/50"
|
||||
)}
|
||||
placeholder="Enter details in German..."
|
||||
/>
|
||||
</div>
|
||||
<div className="col-span-2">
|
||||
<span className="block text-xs text-slate-500 mb-1">Details (EN)</span>
|
||||
<p className="text-sm text-slate-300 line-clamp-2" title={row[COLUMNS.DETAILS_EN]}>{row[COLUMNS.DETAILS_EN] || '-'}</p>
|
||||
<textarea
|
||||
value={formData.detailsEn}
|
||||
onChange={e => setFormData(prev => ({ ...prev, detailsEn: e.target.value }))}
|
||||
className={cn(
|
||||
"w-full h-20 bg-slate-900 border rounded p-2 text-sm text-white focus:outline-none focus:ring-1 transition-colors resize-none",
|
||||
isModified('detailsEn') ? "border-blue-500 focus:ring-blue-500" : "border-slate-700/50"
|
||||
)}
|
||||
placeholder="Enter details in English..."
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ interface ProductDescriptionsProps {
|
||||
onEdit: (index: number) => void;
|
||||
}
|
||||
|
||||
type TabType = 'all' | 'missingDeLong' | 'missingEnLong' | 'missingDeShort' | 'missingEnShort' | 'complete' | 'incomplete';
|
||||
type TabType = 'all' | 'missingDeLong' | 'missingEnLong' | 'missingDeShort' | 'missingEnShort' | 'missingDeDetails' | 'missingEnDetails' | 'complete' | 'incomplete';
|
||||
|
||||
export function ProductDescriptions({ data, onEdit }: ProductDescriptionsProps) {
|
||||
const [activeTab, setActiveTab] = useState<TabType>('all');
|
||||
@@ -31,8 +31,18 @@ export function ProductDescriptions({ data, onEdit }: ProductDescriptionsProps)
|
||||
if (activeTab === 'missingEnLong') result = result.filter(r => !r.row[COLUMNS.LONG_EN]);
|
||||
if (activeTab === 'missingDeShort') result = result.filter(r => !r.row[COLUMNS.SHORT_DE]);
|
||||
if (activeTab === 'missingEnShort') result = result.filter(r => !r.row[COLUMNS.SHORT_EN]);
|
||||
if (activeTab === 'complete') result = result.filter(r => r.row[COLUMNS.LONG_DE] && r.row[COLUMNS.LONG_EN] && r.row[COLUMNS.SHORT_DE] && r.row[COLUMNS.SHORT_EN]);
|
||||
if (activeTab === 'incomplete') result = result.filter(r => !r.row[COLUMNS.LONG_DE] || !r.row[COLUMNS.LONG_EN] || !r.row[COLUMNS.SHORT_DE] || !r.row[COLUMNS.SHORT_EN]);
|
||||
if (activeTab === 'missingDeDetails') result = result.filter(r => !r.row[COLUMNS.DETAILS_DE]);
|
||||
if (activeTab === 'missingEnDetails') result = result.filter(r => !r.row[COLUMNS.DETAILS_EN]);
|
||||
if (activeTab === 'complete') result = result.filter(r =>
|
||||
r.row[COLUMNS.LONG_DE] && r.row[COLUMNS.LONG_EN] &&
|
||||
r.row[COLUMNS.SHORT_DE] && r.row[COLUMNS.SHORT_EN] &&
|
||||
r.row[COLUMNS.DETAILS_DE] && r.row[COLUMNS.DETAILS_EN]
|
||||
);
|
||||
if (activeTab === 'incomplete') result = result.filter(r =>
|
||||
!r.row[COLUMNS.LONG_DE] || !r.row[COLUMNS.LONG_EN] ||
|
||||
!r.row[COLUMNS.SHORT_DE] || !r.row[COLUMNS.SHORT_EN] ||
|
||||
!r.row[COLUMNS.DETAILS_DE] || !r.row[COLUMNS.DETAILS_EN]
|
||||
);
|
||||
|
||||
// Search filter
|
||||
if (search) {
|
||||
@@ -84,9 +94,13 @@ export function ProductDescriptions({ data, onEdit }: ProductDescriptionsProps)
|
||||
return 'bg-yellow-500/10 hover:bg-yellow-500/20'; // EOL Highlight
|
||||
}
|
||||
|
||||
const fields = [row[COLUMNS.LONG_DE], row[COLUMNS.LONG_EN], row[COLUMNS.SHORT_DE], row[COLUMNS.SHORT_EN]];
|
||||
const fields = [
|
||||
row[COLUMNS.LONG_DE], row[COLUMNS.LONG_EN],
|
||||
row[COLUMNS.SHORT_DE], row[COLUMNS.SHORT_EN],
|
||||
row[COLUMNS.DETAILS_DE], row[COLUMNS.DETAILS_EN]
|
||||
];
|
||||
const filled = fields.filter(Boolean).length;
|
||||
if (filled === 4) return 'bg-green-900/10 hover:bg-green-900/20';
|
||||
if (filled === 6) return 'bg-green-900/10 hover:bg-green-900/20';
|
||||
if (filled === 0) return 'bg-red-900/10 hover:bg-red-900/20';
|
||||
return 'bg-yellow-900/10 hover:bg-yellow-900/20';
|
||||
};
|
||||
@@ -111,6 +125,8 @@ export function ProductDescriptions({ data, onEdit }: ProductDescriptionsProps)
|
||||
{ id: 'missingEnLong', label: 'Missing EN Long' },
|
||||
{ id: 'missingDeShort', label: 'Missing DE Short' },
|
||||
{ id: 'missingEnShort', label: 'Missing EN Short' },
|
||||
{ id: 'missingDeDetails', label: 'Missing DE Details' },
|
||||
{ id: 'missingEnDetails', label: 'Missing EN Details' },
|
||||
{ id: 'complete', label: 'Complete' },
|
||||
{ id: 'incomplete', label: 'Incomplete' },
|
||||
];
|
||||
@@ -172,7 +188,8 @@ export function ProductDescriptions({ data, onEdit }: ProductDescriptionsProps)
|
||||
{ col: COLUMNS.ARTICLE_NO, label: 'Article No.' },
|
||||
{ col: COLUMNS.ARTICLE_NAME, label: 'Article Name' },
|
||||
{ col: COLUMNS.LINE, label: 'Line' },
|
||||
{ col: COLUMNS.LICENSE, label: 'License' },
|
||||
{ col: COLUMNS.DETAILS_DE, label: 'Details DE' },
|
||||
{ col: COLUMNS.DETAILS_EN, label: 'Details EN' },
|
||||
{ col: COLUMNS.LONG_DE, label: 'Long DE' },
|
||||
{ col: COLUMNS.LONG_EN, label: 'Long EN' },
|
||||
{ col: COLUMNS.SHORT_DE, label: 'Short DE' },
|
||||
@@ -197,10 +214,11 @@ export function ProductDescriptions({ data, onEdit }: ProductDescriptionsProps)
|
||||
<tbody className="divide-y divide-slate-700/50">
|
||||
{paginatedData.map(({ row, index }) => (
|
||||
<tr key={index} className={cn("transition-colors", getRowColor(row))}>
|
||||
<td className="px-4 py-3 font-mono text-slate-300">{row[COLUMNS.ARTICLE_NO]}</td>
|
||||
<td className="px-4 py-3 font-medium text-white max-w-[300px] truncate" title={row[COLUMNS.ARTICLE_NAME]}>{row[COLUMNS.ARTICLE_NAME]}</td>
|
||||
<td className="px-4 py-3 text-slate-300">{row[COLUMNS.LINE]}</td>
|
||||
<td className="px-4 py-3 text-slate-300">{row[COLUMNS.LICENSE]}</td>
|
||||
<td className="px-4 py-3 font-mono text-slate-300 text-xs">{row[COLUMNS.ARTICLE_NO]}</td>
|
||||
<td className="px-4 py-3 font-medium text-white max-w-[200px] truncate" title={row[COLUMNS.ARTICLE_NAME]}>{row[COLUMNS.ARTICLE_NAME]}</td>
|
||||
<td className="px-4 py-3 text-slate-300 text-xs">{row[COLUMNS.LINE]}</td>
|
||||
<td className="px-4 py-3"><Badge content={row[COLUMNS.DETAILS_DE]} row={row} /></td>
|
||||
<td className="px-4 py-3"><Badge content={row[COLUMNS.DETAILS_EN]} row={row} /></td>
|
||||
<td className="px-4 py-3"><Badge content={row[COLUMNS.LONG_DE]} row={row} /></td>
|
||||
<td className="px-4 py-3"><Badge content={row[COLUMNS.LONG_EN]} row={row} /></td>
|
||||
<td className="px-4 py-3"><Badge content={row[COLUMNS.SHORT_DE]} row={row} /></td>
|
||||
|
||||
Reference in New Issue
Block a user