'use client'; import { useState, useEffect, Suspense, useRef } from 'react'; import { useRouter, useSearchParams } from 'next/navigation'; import Header from '@/components/Header'; import { ScanLine, Search, Check, Box, Layers, AlertCircle, X, History } from 'lucide-react'; import { hasRole } from '@/lib/auth'; import { saveCountOffline, syncOfflineCounts } from '@/lib/offlineSync'; import dynamic from 'next/dynamic'; import { motion, AnimatePresence } from 'framer-motion'; const Scanner = dynamic(() => import('@yudiel/react-qr-scanner').then(mod => mod.Scanner), { ssr: false }); function ItemCountingContent() { const router = useRouter(); const searchParams = useSearchParams(); const product_id = searchParams.get('product_id'); const warehouse = searchParams.get('warehouse'); const [user, setUser] = useState(null); const [settings, setSettings] = useState({}); const [loading, setLoading] = useState(true); // We fetch product details based on product_id from query const [productName, setProductName] = useState(''); const [oldCount, setOldCount] = useState(null); const [newCount, setNewCount] = useState(''); const [shelfCode, setShelfCode] = useState(''); const [submitLoading, setSubmitLoading] = useState(false); const [history, setHistory] = useState([]); const [cameraEnabled, setCameraEnabled] = useState(true); const [camError, setCamError] = useState(''); const [errorMsg, setErrorMsg] = useState(''); const inputRef = useRef(null); useEffect(() => { const userData = localStorage.getItem('user'); if (userData) setUser(JSON.parse(userData)); fetchSettings(); fetchInitialItemData(); window.addEventListener('online', syncOfflineCounts); return () => window.removeEventListener('online', syncOfflineCounts); }, []); const fetchSettings = async () => { try { const res = await fetch('/api/settings'); if (res.ok) setSettings(await res.json()); } catch (e) {} }; const fetchInitialItemData = async () => { if (!product_id) { router.push('/dashboard'); return; } try { const nameRes = await fetch('/api/hesabfa', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ code: product_id, type: 'name' }) }); const nameData = await nameRes.json(); if (!nameData?.Result?.Name || nameData?.Result?.Name === 'نامشخص') { setErrorMsg('کالا یافت نشد.'); } else { setProductName(nameData.Result.Name); } const qRes = await fetch('/api/hesabfa', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ code: product_id, type: 'quantity' }) }); const qData = await qRes.json(); const productInfo = qData?.Result?.[0]; let foundStock = 0; if (productInfo) { if (productInfo.StockByWarehouse && Array.isArray(productInfo.StockByWarehouse)) { const wInfo = productInfo.StockByWarehouse.find(w => w.WarehouseCode === Number(warehouse) || w.Code === Number(warehouse)); if (wInfo) foundStock = wInfo.Stock || wInfo.Quantity || 0; else foundStock = productInfo.Stock || productInfo.Quantity || 0; } else if (productInfo.Warehouse && Array.isArray(productInfo.Warehouse)) { const wInfo = productInfo.Warehouse.find(w => w.Code === Number(warehouse)); foundStock = wInfo?.Quantity ?? productInfo.Stock ?? productInfo.Quantity ?? 0; } else { foundStock = productInfo.Stock ?? productInfo.Quantity ?? 0; } } setOldCount(foundStock); } catch (error) { setErrorMsg('خطا در دریافت اطلاعات کالا از سرور'); } finally { setLoading(false); } }; const handleScan = (detectedCodes) => { if (detectedCodes && detectedCodes.length > 0) { const scannedValue = detectedCodes[0].rawValue; // We do not close the camera! setShelfCode(scannedValue); setCamError(''); if (inputRef.current) setTimeout(() => inputRef.current.focus(), 100); } }; const handleError = (error) => { const msg = error?.message || error?.name || ''; if (msg.includes('Requested device not found')) setCamError('دوربینی یافت نشد.'); else setCamError('خطا در دسترسی به دوربین.'); }; const handleSubmitShelf = async () => { if (!shelfCode) { setErrorMsg('کد قفسه را وارد یا اسکن کنید'); return; } if (newCount === '' || newCount === null) { setErrorMsg('تعداد را وارد کنید'); return; } setSubmitLoading(true); setErrorMsg(''); const payload = { product_id: String(product_id), product_name: productName, warehouse: Number(warehouse), shelfCode: shelfCode.toUpperCase(), old_count: oldCount || 0, new_count: Number(newCount), user_id: user?.id, mode: 'ITEM' }; try { const res = await fetch('/api/counting', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload) }); if (res.ok) { setHistory([{ shelf: shelfCode.toUpperCase(), count: newCount }, ...history]); setShelfCode(''); setNewCount(''); setErrorMsg(''); } else { const data = await res.json(); setErrorMsg(data.error || 'خطا در ثبت اطلاعات در سرور'); } } catch (err) { await saveCountOffline(payload); alert('ارتباط با سرور قطع است. اطلاعات ذخیره شد.'); setHistory([{ shelf: shelfCode.toUpperCase(), count: newCount, offline: true }, ...history]); setShelfCode(''); setNewCount(''); } finally { setSubmitLoading(false); } }; const handleCancelItem = async () => { const reason = window.prompt('لطفاً دلیل لغو انبارگردانی این کالا را وارد کنید:'); if (!reason) return; try { await fetch('/api/counting/cancel', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ product_id, warehouse, userId: user?.id, reason, mode: 'ITEM' }) }); router.push('/dashboard'); } catch (error) { alert('خطا در ارتباط با سرور'); } }; const isBlind = settings?.blind_counting && !hasRole(user?.roles, ['ADMIN', 'SUPERVISOR']); if (loading) { return (
{item.shelf}