mirror of
https://github.com/christianvidalwolf-prog/Craze-Data-check.git
synced 2026-08-03 11:45:24 +02:00
feat: restrict description column filters to Present/Missing only
Long DE, Long EN, Short DE, and Short EN columns now only show Present/Missing filter options instead of all unique values Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
This commit is contained in:
co-authored by
Qwen-Coder
parent
976f38eef8
commit
20a0c2d786
@@ -1,4 +1,4 @@
|
|||||||
import React, { useState, useMemo } from 'react';
|
import React, { useState, useMemo, useCallback } from 'react';
|
||||||
import { ExcelRow, COLUMNS } from '../types';
|
import { ExcelRow, COLUMNS } from '../types';
|
||||||
import { Search, Filter, Edit2, ChevronDown, ChevronUp, X } from 'lucide-react';
|
import { Search, Filter, Edit2, ChevronDown, ChevronUp, X } from 'lucide-react';
|
||||||
import { cn } from '../lib/utils';
|
import { cn } from '../lib/utils';
|
||||||
@@ -11,6 +11,9 @@ interface ProductDescriptionsProps {
|
|||||||
|
|
||||||
type TabType = 'all' | 'missingLongDE' | 'missingLongEN' | 'missingLongAny' | 'missingShortDE' | 'missingShortEN' | 'missingShortAny' | 'complete' | 'incomplete';
|
type TabType = 'all' | 'missingLongDE' | 'missingLongEN' | 'missingLongAny' | 'missingShortDE' | 'missingShortEN' | 'missingShortAny' | 'complete' | 'incomplete';
|
||||||
|
|
||||||
|
// Description columns that should only have Present/Missing filters
|
||||||
|
const DESCRIPTION_COLUMNS = [COLUMNS.LONG_DE, COLUMNS.LONG_EN, COLUMNS.SHORT_DE, COLUMNS.SHORT_EN];
|
||||||
|
|
||||||
export function ProductDescriptions({ data, onEdit }: ProductDescriptionsProps) {
|
export function ProductDescriptions({ data, onEdit }: ProductDescriptionsProps) {
|
||||||
const [activeTab, setActiveTab] = useState<TabType>('all');
|
const [activeTab, setActiveTab] = useState<TabType>('all');
|
||||||
const [search, setSearch] = useState('');
|
const [search, setSearch] = useState('');
|
||||||
@@ -36,20 +39,20 @@ export function ProductDescriptions({ data, onEdit }: ProductDescriptionsProps)
|
|||||||
if (activeTab === 'missingShortDE') result = result.filter(r => !r.row[COLUMNS.SHORT_DE]);
|
if (activeTab === 'missingShortDE') result = result.filter(r => !r.row[COLUMNS.SHORT_DE]);
|
||||||
if (activeTab === 'missingShortEN') result = result.filter(r => !r.row[COLUMNS.SHORT_EN]);
|
if (activeTab === 'missingShortEN') result = result.filter(r => !r.row[COLUMNS.SHORT_EN]);
|
||||||
if (activeTab === 'missingShortAny') result = result.filter(r => !r.row[COLUMNS.SHORT_DE] || !r.row[COLUMNS.SHORT_EN]);
|
if (activeTab === 'missingShortAny') result = result.filter(r => !r.row[COLUMNS.SHORT_DE] || !r.row[COLUMNS.SHORT_EN]);
|
||||||
|
|
||||||
if (activeTab === 'complete') result = result.filter(r =>
|
if (activeTab === 'complete') result = result.filter(r =>
|
||||||
r.row[COLUMNS.LONG_DE] && r.row[COLUMNS.LONG_EN] &&
|
r.row[COLUMNS.LONG_DE] && r.row[COLUMNS.LONG_EN] &&
|
||||||
r.row[COLUMNS.SHORT_DE] && r.row[COLUMNS.SHORT_EN]
|
r.row[COLUMNS.SHORT_DE] && r.row[COLUMNS.SHORT_EN]
|
||||||
);
|
);
|
||||||
if (activeTab === 'incomplete') result = result.filter(r =>
|
if (activeTab === 'incomplete') result = result.filter(r =>
|
||||||
!r.row[COLUMNS.LONG_DE] || !r.row[COLUMNS.LONG_EN] ||
|
!r.row[COLUMNS.LONG_DE] || !r.row[COLUMNS.LONG_EN] ||
|
||||||
!r.row[COLUMNS.SHORT_DE] || !r.row[COLUMNS.SHORT_EN]
|
!r.row[COLUMNS.SHORT_DE] || !r.row[COLUMNS.SHORT_EN]
|
||||||
);
|
);
|
||||||
|
|
||||||
// Search filter
|
// Search filter
|
||||||
if (search) {
|
if (search) {
|
||||||
const s = search.toLowerCase();
|
const s = search.toLowerCase();
|
||||||
result = result.filter(r =>
|
result = result.filter(r =>
|
||||||
String(r.row[COLUMNS.ARTICLE_NO] || '').toLowerCase().includes(s) ||
|
String(r.row[COLUMNS.ARTICLE_NO] || '').toLowerCase().includes(s) ||
|
||||||
String(r.row[COLUMNS.ARTICLE_NAME] || '').toLowerCase().includes(s)
|
String(r.row[COLUMNS.ARTICLE_NAME] || '').toLowerCase().includes(s)
|
||||||
);
|
);
|
||||||
@@ -61,9 +64,20 @@ export function ProductDescriptions({ data, onEdit }: ProductDescriptionsProps)
|
|||||||
|
|
||||||
// Column-specific filters (Excel-like)
|
// Column-specific filters (Excel-like)
|
||||||
Object.entries(columnFilters).forEach(([colIdx, selectedValues]) => {
|
Object.entries(columnFilters).forEach(([colIdx, selectedValues]) => {
|
||||||
|
const col = Number(colIdx);
|
||||||
const vals = selectedValues as string[];
|
const vals = selectedValues as string[];
|
||||||
if (vals.length > 0) {
|
if (vals.length > 0) {
|
||||||
result = result.filter(r => vals.includes(String(r.row[Number(colIdx)] || '')));
|
// For description columns, filter by present/missing
|
||||||
|
if (DESCRIPTION_COLUMNS.includes(col)) {
|
||||||
|
result = result.filter(r => {
|
||||||
|
const hasValue = Boolean(r.row[col]);
|
||||||
|
const shouldInclude = vals.includes('Present') && hasValue || vals.includes('Missing') && !hasValue;
|
||||||
|
return shouldInclude;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// For other columns, use regular value matching
|
||||||
|
result = result.filter(r => vals.includes(String(r.row[col] || '')));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -96,6 +110,11 @@ export function ProductDescriptions({ data, onEdit }: ProductDescriptionsProps)
|
|||||||
};
|
};
|
||||||
|
|
||||||
const getUniqueValues = (col: number) => {
|
const getUniqueValues = (col: number) => {
|
||||||
|
// For description columns, return only 'Present' and 'Missing'
|
||||||
|
if (DESCRIPTION_COLUMNS.includes(col)) {
|
||||||
|
return ['Present', 'Missing'];
|
||||||
|
}
|
||||||
|
// For other columns, return actual unique values
|
||||||
const values = data.map(r => String(r[col] || ''));
|
const values = data.map(r => String(r[col] || ''));
|
||||||
return Array.from(new Set(values)).sort();
|
return Array.from(new Set(values)).sort();
|
||||||
};
|
};
|
||||||
@@ -126,7 +145,7 @@ export function ProductDescriptions({ data, onEdit }: ProductDescriptionsProps)
|
|||||||
}
|
}
|
||||||
|
|
||||||
const fields = [
|
const fields = [
|
||||||
row[COLUMNS.LONG_DE], row[COLUMNS.LONG_EN],
|
row[COLUMNS.LONG_DE], row[COLUMNS.LONG_EN],
|
||||||
row[COLUMNS.SHORT_DE], row[COLUMNS.SHORT_EN]
|
row[COLUMNS.SHORT_DE], row[COLUMNS.SHORT_EN]
|
||||||
];
|
];
|
||||||
const filled = fields.filter(Boolean).length;
|
const filled = fields.filter(Boolean).length;
|
||||||
@@ -137,13 +156,13 @@ export function ProductDescriptions({ data, onEdit }: ProductDescriptionsProps)
|
|||||||
|
|
||||||
const Badge = ({ content, row }: { content: any, row: ExcelRow }) => {
|
const Badge = ({ content, row }: { content: any, row: ExcelRow }) => {
|
||||||
if (content) return <span className="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-green-500/20 text-green-400 border border-green-500/30">✓</span>;
|
if (content) return <span className="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-green-500/20 text-green-400 border border-green-500/30">✓</span>;
|
||||||
|
|
||||||
// EOL Exception: OOC and stock <= 0
|
// EOL Exception: OOC and stock <= 0
|
||||||
const classification = String(row[COLUMNS.CLASSIFICATION] || '').toUpperCase().trim();
|
const classification = String(row[COLUMNS.CLASSIFICATION] || '').toUpperCase().trim();
|
||||||
const stock = Number(row[COLUMNS.ITEM_AVAILABLE] || 0);
|
const stock = Number(row[COLUMNS.ITEM_AVAILABLE] || 0);
|
||||||
|
|
||||||
if (classification === 'OOC' && stock <= 0) {
|
if (classification === 'OOC' && stock <= 0) {
|
||||||
return <span className="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-yellow-500/20 text-yellow-400 border border-yellow-500/30">EOL not neccessary</span>;
|
return <span className="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-yellow-500/20 text-yellow-400 border border-yellow-500/30">EOL not neccessary</span>;
|
||||||
}
|
}
|
||||||
|
|
||||||
return <span className="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-red-500/20 text-red-400 border border-red-500/30">✗ Missing</span>;
|
return <span className="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-red-500/20 text-red-400 border border-red-500/30">✗ Missing</span>;
|
||||||
@@ -170,8 +189,8 @@ export function ProductDescriptions({ data, onEdit }: ProductDescriptionsProps)
|
|||||||
onClick={() => { setActiveTab(tab.id); setPage(1); }}
|
onClick={() => { setActiveTab(tab.id); setPage(1); }}
|
||||||
className={cn(
|
className={cn(
|
||||||
"px-4 py-2 rounded-md text-sm font-medium transition-colors",
|
"px-4 py-2 rounded-md text-sm font-medium transition-colors",
|
||||||
activeTab === tab.id
|
activeTab === tab.id
|
||||||
? "bg-blue-600 text-white shadow-md"
|
? "bg-blue-600 text-white shadow-md"
|
||||||
: "bg-slate-800 text-slate-400 hover:bg-slate-700 hover:text-white"
|
: "bg-slate-800 text-slate-400 hover:bg-slate-700 hover:text-white"
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
@@ -237,8 +256,8 @@ export function ProductDescriptions({ data, onEdit }: ProductDescriptionsProps)
|
|||||||
{ col: COLUMNS.SHORT_DE, label: 'Short DE' },
|
{ col: COLUMNS.SHORT_DE, label: 'Short DE' },
|
||||||
{ col: COLUMNS.SHORT_EN, label: 'Short EN' },
|
{ col: COLUMNS.SHORT_EN, label: 'Short EN' },
|
||||||
].map(({ col, label }) => (
|
].map(({ col, label }) => (
|
||||||
<th
|
<th
|
||||||
key={col}
|
key={col}
|
||||||
className="px-4 py-3 font-medium transition-colors select-none group relative"
|
className="px-4 py-3 font-medium transition-colors select-none group relative"
|
||||||
>
|
>
|
||||||
<div className="flex items-center justify-between gap-1">
|
<div className="flex items-center justify-between gap-1">
|
||||||
@@ -294,8 +313,8 @@ export function ProductDescriptions({ data, onEdit }: ProductDescriptionsProps)
|
|||||||
<td className="px-4 py-3">
|
<td className="px-4 py-3">
|
||||||
<span className={cn(
|
<span className={cn(
|
||||||
"px-2 py-0.5 rounded text-[10px] font-bold border",
|
"px-2 py-0.5 rounded text-[10px] font-bold border",
|
||||||
String(row[COLUMNS.CLASSIFICATION]).includes('OOC')
|
String(row[COLUMNS.CLASSIFICATION]).includes('OOC')
|
||||||
? "bg-amber-500/10 text-amber-500 border-amber-500/20"
|
? "bg-amber-500/10 text-amber-500 border-amber-500/20"
|
||||||
: "bg-slate-700/50 text-slate-400 border-slate-600/50"
|
: "bg-slate-700/50 text-slate-400 border-slate-600/50"
|
||||||
)}>
|
)}>
|
||||||
{row[COLUMNS.CLASSIFICATION] || '—'}
|
{row[COLUMNS.CLASSIFICATION] || '—'}
|
||||||
@@ -326,7 +345,7 @@ export function ProductDescriptions({ data, onEdit }: ProductDescriptionsProps)
|
|||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="bg-slate-900 border-t border-slate-700 p-4 flex items-center justify-between text-sm text-slate-400">
|
<div className="bg-slate-900 border-t border-slate-700 p-4 flex items-center justify-between text-sm text-slate-400">
|
||||||
<div className="flex items-center gap-4">
|
<div className="flex items-center gap-4">
|
||||||
<span>Showing {Math.min((page - 1) * pageSize + 1, filteredData.length)} to {Math.min(page * pageSize, filteredData.length)} of {filteredData.length} entries</span>
|
<span>Showing {Math.min((page - 1) * pageSize + 1, filteredData.length)} to {Math.min(page * pageSize, filteredData.length)} of {filteredData.length} entries</span>
|
||||||
|
|||||||
Reference in New Issue
Block a user