'use client'; import { useState, useEffect, Suspense } from 'react'; import { useRouter, useSearchParams } from 'next/navigation'; import Header from '@/components/Header'; import { Lock, Unlock, ScanLine, Search, Check, Box, Layers, AlertCircle, X } 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 ShelfCountingContent() { const router = useRouter(); const searchParams = useSearchParams(); const shelfCode = searchParams.get('code'); const warehouse = searchParams.get('warehouse'); const [user, setUser] = useState(null); const [settings, setSettings] = useState({}); const [loading, setLoading] = useState(true); // Item scanning state const [productCode, setProductCode] = useState(''); const [productName, setProductName] = useState(''); const [oldCount, setOldCount] = useState(null); const [newCount, setNewCount] = useState(''); const [itemLoading, setItemLoading] = useState(false); const [submitLoading, setSubmitLoading] = useState(false); const [errorMsg, setErrorMsg] = useState(''); const [cameraEnabled, setCameraEnabled] = useState(true); const [camError, setCamError] = useState(''); // History of scanned items in this session const [history, setHistory] = useState([]); useEffect(() => { const userData = localStorage.getItem('user'); if (userData) setUser(JSON.parse(userData)); fetchSettings(); window.addEventListener('online', syncOfflineCounts); return () => window.removeEventListener('online', syncOfflineCounts); }, []); const fetchSettings = async () => { try { const res = await fetch('/api/settings'); if (res.ok) { const data = await res.json(); setSettings(data); } } catch (e) { console.error(e); } finally { setLoading(false); } }; const handleFinishShelf = async () => { try { const token = localStorage.getItem('token'); await fetch('/api/locations/lock', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` }, body: JSON.stringify({ shelfCode, action: 'UNLOCK' }) }); router.push('/dashboard'); } catch (error) { router.push('/dashboard'); } }; const fetchItemData = async (codeToFetch) => { const code = codeToFetch || productCode; if (!code) { setErrorMsg('کد کالا را وارد کنید'); return; } setErrorMsg(''); setItemLoading(true); setProductName(''); setOldCount(null); setNewCount(''); try { const nameRes = await fetch('/api/hesabfa', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ code, type: 'name' }) }); const nameData = await nameRes.json(); if (!nameData?.Result?.Name || nameData?.Result?.Name === 'نامشخص' || nameData.error) { setErrorMsg('کالایی با این کد در حسابفا یافت نشد.'); setItemLoading(false); return; } setProductName(nameData.Result.Name); const qRes = await fetch('/api/hesabfa', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ code, type: 'quantity' }) }); const qData = await qRes.json(); const productInfo = qData?.Result?.[0]; const wInfo = productInfo?.Warehouse?.find(w => w.Code === Number(warehouse)); setOldCount(wInfo?.Quantity ?? 0); } catch (error) { console.error(error); setErrorMsg('خطا در ارتباط با حسابفا'); } finally { setItemLoading(false); } }; const handleProductCodeChange = (e) => { setProductCode(e.target.value); setErrorMsg(''); setProductName(''); }; const handleProductCodeKeyDown = (e) => { if (e.key === 'Enter') { fetchItemData(); } }; const handleScan = (detectedCodes) => { if (detectedCodes && detectedCodes.length > 0) { const scannedValue = detectedCodes[0].rawValue; setProductCode(scannedValue); setCameraEnabled(false); setCamError(''); fetchItemData(scannedValue); } }; const handleError = (error) => { const msg = error?.message || error?.name || ''; if (msg.includes('Requested device not found')) { setCamError('دوربینی یافت نشد.'); } else { setCamError('خطا در دسترسی به دوربین.'); } }; const handleSubmitItem = async () => { if (!productName || errorMsg) { setErrorMsg('ابتدا از صحت کالا اطمینان حاصل کنید'); return; } if (newCount === '' || newCount === null) { setErrorMsg('لطفاً تعداد را وارد کنید'); return; } setSubmitLoading(true); const payload = { product_id: productCode, product_name: productName, warehouse, shelfCode: shelfCode.toUpperCase(), old_count: oldCount || 0, new_count: Number(newCount), user_id: user?.id, mode: 'SHELF' }; try { const res = await fetch('/api/counting', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload) }); if (res.ok) { setHistory([{ code: productCode, name: productName, count: newCount }, ...history]); setProductCode(''); setProductName(''); setOldCount(null); setNewCount(''); setErrorMsg(''); } else { setErrorMsg('خطا در ثبت شمارش کالا در سرور'); } } catch (err) { // Network error (offline or server down) await saveCountOffline(payload); alert('ارتباط با سرور قطع است. اطلاعات در گوشی شما ذخیره شد و به محض اتصال ارسال میشود.'); setHistory([{ code: productCode, name: productName, count: newCount, offline: true }, ...history]); setProductCode(''); setProductName(''); setOldCount(null); setNewCount(''); setErrorMsg(''); } finally { setSubmitLoading(false); } }; const handleCancelShelf = async () => { const reason = window.prompt('لطفاً دلیل لغو انبارگردانی این قفسه را وارد کنید:'); if (!reason) return; try { await fetch('/api/counting/cancel', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ shelfCode, warehouse, userId: user?.id, reason, mode: 'SHELF' }) }); router.push('/dashboard'); } catch (error) { alert('خطا در لغو انبارگردانی'); } }; const isBlind = settings?.blind_counting && !hasRole(user?.roles, ['ADMIN', 'SUPERVISOR']); if (loading) { return (
کد حسابفا: {productCode}
{item.name}
کد: {item.code}