fix: DiffTable colonnes visibles + filtrage lignes parasites dans Sheets
- table-fixed avec colgroup pour éviter que la colonne Ref écrase les autres - overflow-x-auto pour permettre le scroll si nécessaire - parseSheetRows : ignore les refs invalides (< 2 chars, caractères spéciaux, mots réservés) - parseSheetRows : ignore les lignes sans prix valide (séparateurs, notes) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -56,28 +56,33 @@ export function DiffTable({ diff }: DiffTableProps) {
|
|||||||
{paginated.length === 0 ? (
|
{paginated.length === 0 ? (
|
||||||
<p className="text-slate-600 text-sm py-4 text-center">Aucun changement dans cette catégorie.</p>
|
<p className="text-slate-600 text-sm py-4 text-center">Aucun changement dans cette catégorie.</p>
|
||||||
) : (
|
) : (
|
||||||
<div className="border border-slate-700 rounded-xl overflow-hidden">
|
<div className="border border-slate-700 rounded-xl overflow-x-auto">
|
||||||
<table className="w-full text-xs">
|
<table className="w-full text-xs table-fixed">
|
||||||
|
<colgroup>
|
||||||
|
<col className="w-36" />
|
||||||
|
<col className="w-32" />
|
||||||
|
<col />
|
||||||
|
</colgroup>
|
||||||
<thead className="bg-slate-800 text-slate-400">
|
<thead className="bg-slate-800 text-slate-400">
|
||||||
<tr>
|
<tr>
|
||||||
<th className="px-3 py-2 text-left">Ref</th>
|
<th className="px-3 py-2 text-left">Ref</th>
|
||||||
<th className="px-3 py-2 text-left">Type</th>
|
<th className="px-3 py-2 text-left">Type</th>
|
||||||
<th className="px-3 py-2 text-left">Champs modifiés</th>
|
<th className="px-3 py-2 text-left">Détail</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{paginated.map((change) => (
|
{paginated.map((change) => (
|
||||||
<tr key={change.ref} className={`border-t border-slate-800 ${TYPE_CONFIG[change.type].bg}`}>
|
<tr key={change.ref} className={`border-t border-slate-800 ${TYPE_CONFIG[change.type].bg}`}>
|
||||||
<td className="px-3 py-2 font-mono text-slate-300">{change.ref}</td>
|
<td className="px-3 py-2 font-mono text-slate-300 truncate max-w-0" title={change.ref}>{change.ref}</td>
|
||||||
<td className={`px-3 py-2 font-semibold ${TYPE_CONFIG[change.type].color}`}>
|
<td className={`px-3 py-2 font-semibold whitespace-nowrap ${TYPE_CONFIG[change.type].color}`}>
|
||||||
{TYPE_CONFIG[change.type].label}
|
{TYPE_CONFIG[change.type].label}
|
||||||
</td>
|
</td>
|
||||||
<td className="px-3 py-2 text-slate-400">
|
<td className="px-3 py-2 text-slate-400">
|
||||||
{change.type === 'update' && change.changedFields?.map((f) => (
|
{change.type === 'update' && change.changedFields?.map((f) => (
|
||||||
<span key={f.field} className="inline-block mr-2">
|
<span key={f.field} className="inline-block mr-3">
|
||||||
<span className="text-slate-500">{f.field} : </span>
|
<span className="text-slate-500">{f.field} : </span>
|
||||||
<span className="line-through text-red-400">{f.from}</span>
|
<span className="line-through text-red-400 mr-1">{f.from}</span>
|
||||||
<span className="text-slate-400"> → </span>
|
<span className="text-slate-500 mr-1">→</span>
|
||||||
<span className="text-green-400">{f.to}</span>
|
<span className="text-green-400">{f.to}</span>
|
||||||
</span>
|
</span>
|
||||||
))}
|
))}
|
||||||
|
|||||||
+14
-2
@@ -12,21 +12,33 @@ function normalizeHandle(ref: string): string {
|
|||||||
* Colonnes : A=Référence, B=Nom, C=Description, D=Prix, E=Stock, F=Statut
|
* Colonnes : A=Référence, B=Nom, C=Description, D=Prix, E=Stock, F=Statut
|
||||||
* Exporté pour les tests unitaires.
|
* Exporté pour les tests unitaires.
|
||||||
*/
|
*/
|
||||||
|
const HEADER_WORDS = new Set(['id', 'ref', 'sku', 'nom', 'titre', 'reference', 'prix', 'statut'])
|
||||||
|
|
||||||
|
function isValidRef(ref: string): boolean {
|
||||||
|
if (ref.length < 2) return false
|
||||||
|
if (/^[^a-zA-Z0-9]+$/.test(ref)) return false // que des caractères spéciaux
|
||||||
|
if (HEADER_WORDS.has(ref.toLowerCase())) return false
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
export function parseSheetRows(rows: string[][]): SyncProduct[] {
|
export function parseSheetRows(rows: string[][]): SyncProduct[] {
|
||||||
return rows
|
return rows
|
||||||
.map((row): SyncProduct | null => {
|
.map((row): SyncProduct | null => {
|
||||||
const ref = row[0]?.trim()
|
const ref = row[0]?.trim()
|
||||||
if (!ref) return null
|
if (!ref || !isValidRef(ref)) return null
|
||||||
|
|
||||||
const rawPrice = (row[3] ?? '').replace(',', '.').trim()
|
const rawPrice = (row[3] ?? '').replace(',', '.').trim()
|
||||||
const price = parseFloat(rawPrice || '0')
|
const price = parseFloat(rawPrice || '0')
|
||||||
|
|
||||||
|
// Ligne sans prix valide = séparateur ou note, pas un produit
|
||||||
|
if (isNaN(price) || price <= 0) return null
|
||||||
|
|
||||||
return {
|
return {
|
||||||
ref,
|
ref,
|
||||||
handle: normalizeHandle(ref),
|
handle: normalizeHandle(ref),
|
||||||
title: row[1]?.trim() || ref,
|
title: row[1]?.trim() || ref,
|
||||||
description: row[2]?.trim() || '',
|
description: row[2]?.trim() || '',
|
||||||
price: isNaN(price) ? '0.00' : price.toFixed(2),
|
price: price.toFixed(2),
|
||||||
stock: parseInt(row[4] ?? '0', 10) || 0,
|
stock: parseInt(row[4] ?? '0', 10) || 0,
|
||||||
status: (row[5] ?? '').toLowerCase().includes('inac') ? 'draft' : 'active',
|
status: (row[5] ?? '').toLowerCase().includes('inac') ? 'draft' : 'active',
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user