mirror of
https://github.com/christianvidalwolf-prog/Craze-Data-check.git
synced 2026-08-03 11:05:24 +02:00
Only show Total, Missing DE/EN Long/Short, Fully Complete header when viewing Product Descriptions tab
206 lines
9.1 KiB
TypeScript
206 lines
9.1 KiB
TypeScript
import React, { useState, useRef, useEffect } from 'react';
|
|
import { Download, LogOut, Undo2, CloudUpload, Loader2, ChevronDown, RotateCcw } from 'lucide-react';
|
|
import { cn } from '../lib/utils';
|
|
|
|
interface TopBarProps {
|
|
stats: any;
|
|
activeModule?: string;
|
|
onExport: () => void;
|
|
hasData: boolean;
|
|
hasUnsavedChanges: boolean;
|
|
userEmail?: string;
|
|
onSignOut?: () => void;
|
|
canUndo: boolean;
|
|
onUndo: () => void;
|
|
undoMessage?: string;
|
|
undoSteps: number;
|
|
pendingCount: number;
|
|
pendingChanges: Record<string, { articleName: string }>;
|
|
onSaveAll: () => Promise<void>;
|
|
onRevertRow: (articleNo: string) => void;
|
|
isSavingAll: boolean;
|
|
}
|
|
|
|
export function TopBar({ stats, activeModule, onExport, hasData, hasUnsavedChanges, userEmail, onSignOut, canUndo, onUndo, undoMessage, undoSteps, pendingCount, pendingChanges, onSaveAll, onRevertRow, isSavingAll }: TopBarProps) {
|
|
const [showPending, setShowPending] = useState(false);
|
|
const dropdownRef = useRef<HTMLDivElement>(null);
|
|
|
|
useEffect(() => {
|
|
if (!showPending) return;
|
|
const handler = (e: MouseEvent) => {
|
|
if (dropdownRef.current && !dropdownRef.current.contains(e.target as Node)) {
|
|
setShowPending(false);
|
|
}
|
|
};
|
|
document.addEventListener('mousedown', handler);
|
|
return () => document.removeEventListener('mousedown', handler);
|
|
}, [showPending]);
|
|
|
|
// Close dropdown when all changes are saved/reverted
|
|
useEffect(() => {
|
|
if (pendingCount === 0) setShowPending(false);
|
|
}, [pendingCount]);
|
|
|
|
return (
|
|
<header className="bg-[#020812] border-b border-slate-700/30 h-32 flex items-center justify-between px-10 shrink-0 z-10 shadow-2xl">
|
|
<div className="flex items-center -ml-4">
|
|
<img
|
|
src="/logo.png"
|
|
alt="Craze Scan"
|
|
className="h-24 w-auto object-contain filter brightness-110 contrast-125 hover:scale-105 transition-transform duration-300"
|
|
/>
|
|
</div>
|
|
|
|
{stats && activeModule === 'descriptions' && (
|
|
<div className="flex items-center gap-4 text-xs font-medium">
|
|
<div className="flex flex-col items-center">
|
|
<span className="text-slate-400">Total</span>
|
|
<span className="bg-slate-700 text-white px-2 py-0.5 rounded mt-1">{stats.total}</span>
|
|
</div>
|
|
<div className="flex flex-col items-center">
|
|
<span className="text-slate-400">Missing DE Long</span>
|
|
<span className="bg-red-500/20 text-red-400 px-2 py-0.5 rounded border border-red-500/30 mt-1">{stats.missingDeLong}</span>
|
|
</div>
|
|
<div className="flex flex-col items-center">
|
|
<span className="text-slate-400">Missing EN Long</span>
|
|
<span className="bg-orange-500/20 text-orange-400 px-2 py-0.5 rounded border border-orange-500/30 mt-1">{stats.missingEnLong}</span>
|
|
</div>
|
|
<div className="flex flex-col items-center">
|
|
<span className="text-slate-400">Missing DE Short</span>
|
|
<span className="bg-orange-500/20 text-orange-400 px-2 py-0.5 rounded border border-orange-500/30 mt-1">{stats.missingDeShort}</span>
|
|
</div>
|
|
<div className="flex flex-col items-center">
|
|
<span className="text-slate-400">Missing EN Short</span>
|
|
<span className="bg-orange-500/20 text-orange-400 px-2 py-0.5 rounded border border-orange-500/30 mt-1">{stats.missingEnShort}</span>
|
|
</div>
|
|
<div className="flex flex-col items-center">
|
|
<span className="text-slate-400">Fully Complete</span>
|
|
<span className="bg-green-500/20 text-green-400 px-2 py-0.5 rounded border border-green-500/30 mt-1">{stats.fullyComplete}</span>
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
<div className="flex items-center gap-3">
|
|
<div className="relative" ref={dropdownRef}>
|
|
{/* Split button: Save All + dropdown toggle */}
|
|
<div className={cn(
|
|
"flex items-center rounded-md overflow-hidden shadow-lg transition-all",
|
|
pendingCount > 0 ? "shadow-green-900/30" : "shadow-none opacity-40"
|
|
)}>
|
|
<button
|
|
onClick={onSaveAll}
|
|
disabled={isSavingAll || pendingCount === 0}
|
|
className={cn(
|
|
"flex items-center gap-2 px-4 py-2 text-sm font-bold transition-all text-white",
|
|
pendingCount > 0
|
|
? "bg-green-600 hover:bg-green-500 disabled:opacity-60"
|
|
: "bg-slate-700 cursor-not-allowed"
|
|
)}
|
|
>
|
|
{isSavingAll ? <Loader2 className="w-4 h-4 animate-spin" /> : <CloudUpload className="w-4 h-4" />}
|
|
{isSavingAll
|
|
? 'Saving...'
|
|
: pendingCount > 0
|
|
? `Save ${pendingCount} change${pendingCount > 1 ? 's' : ''}`
|
|
: 'No pending changes'}
|
|
</button>
|
|
<button
|
|
onClick={() => pendingCount > 0 && setShowPending(v => !v)}
|
|
disabled={isSavingAll || pendingCount === 0}
|
|
className={cn(
|
|
"flex items-center px-2 py-2 text-white border-l transition-all",
|
|
pendingCount > 0
|
|
? "bg-green-700 hover:bg-green-600 border-green-500/40"
|
|
: "bg-slate-700 cursor-not-allowed border-slate-600"
|
|
)}
|
|
title="View pending changes"
|
|
>
|
|
<ChevronDown className={cn("w-4 h-4 transition-transform", showPending && "rotate-180")} />
|
|
</button>
|
|
</div>
|
|
|
|
{/* Dropdown: list of pending changes */}
|
|
{showPending && pendingCount > 0 && (
|
|
<div className="absolute right-0 top-full mt-2 w-80 bg-slate-800 border border-slate-700 rounded-lg shadow-2xl z-50 overflow-hidden">
|
|
<div className="px-3 py-2 border-b border-slate-700 flex items-center justify-between">
|
|
<span className="text-xs font-bold text-slate-400 uppercase tracking-wider">Pending changes</span>
|
|
<span className="text-xs text-slate-500">{pendingCount} unsaved</span>
|
|
</div>
|
|
<div className="max-h-64 overflow-y-auto">
|
|
{Object.entries(pendingChanges).map(([articleNo, { articleName }]) => (
|
|
<div
|
|
key={articleNo}
|
|
className="flex items-center gap-2 px-3 py-2.5 hover:bg-slate-700/50 border-b border-slate-700/50 last:border-0"
|
|
>
|
|
<div className="w-2 h-2 rounded-full bg-yellow-400 shrink-0" />
|
|
<div className="flex-1 min-w-0">
|
|
<p className="text-xs font-mono text-slate-400">{articleNo}</p>
|
|
<p className="text-sm text-white truncate">{articleName}</p>
|
|
</div>
|
|
<button
|
|
onClick={() => onRevertRow(articleNo)}
|
|
title="Discard this change"
|
|
className="shrink-0 flex items-center gap-1 px-2 py-1 text-xs text-red-400 hover:text-white hover:bg-red-600 rounded transition-colors"
|
|
>
|
|
<RotateCcw className="w-3 h-3" />
|
|
Revert
|
|
</button>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
)}
|
|
</div>
|
|
|
|
{hasData && (
|
|
<button
|
|
onClick={onExport}
|
|
className={`flex items-center gap-2 px-4 py-2 rounded-md text-sm font-medium transition-colors ${
|
|
hasUnsavedChanges
|
|
? 'bg-blue-600 hover:bg-blue-700 text-white'
|
|
: 'bg-slate-700 hover:bg-slate-600 text-slate-200'
|
|
}`}
|
|
>
|
|
<Download className="w-4 h-4" />
|
|
Export Updated Excel
|
|
{hasUnsavedChanges && <span className="w-2 h-2 rounded-full bg-red-500 ml-1 animate-pulse" />}
|
|
</button>
|
|
)}
|
|
|
|
<button
|
|
onClick={onUndo}
|
|
disabled={!canUndo}
|
|
title={canUndo ? `Undo: ${undoMessage}` : 'No changes to undo'}
|
|
className={cn(
|
|
"flex items-center gap-2 px-4 py-2 rounded-md text-sm font-bold transition-all shadow-lg relative group",
|
|
canUndo
|
|
? "bg-amber-600 hover:bg-amber-700 text-white shadow-amber-900/40 cursor-pointer"
|
|
: "bg-slate-800 text-slate-600 shadow-none cursor-not-allowed opacity-50"
|
|
)}
|
|
>
|
|
<Undo2 className="w-4 h-4" />
|
|
BACK / UNDO
|
|
{canUndo && undoSteps > 1 && (
|
|
<span className="absolute -top-1 -right-1 bg-white text-amber-700 text-[10px] w-4 h-4 rounded-full flex items-center justify-center shadow-md font-bold">
|
|
{undoSteps}
|
|
</span>
|
|
)}
|
|
</button>
|
|
|
|
{userEmail && (
|
|
<div className="flex items-center gap-2 border-l border-slate-700 pl-3">
|
|
<span className="text-xs text-slate-400">{userEmail}</span>
|
|
<button
|
|
onClick={onSignOut}
|
|
title="Sign out"
|
|
className="p-1.5 text-slate-400 hover:text-white hover:bg-slate-700 rounded-md transition-colors"
|
|
>
|
|
<LogOut className="w-4 h-4" />
|
|
</button>
|
|
</div>
|
|
)}
|
|
</div>
|
|
</header>
|
|
);
|
|
}
|