+ )}
{/* Bottom: Actions */}
- ))}
+ );
+ })}
- {filteredDiscrepancies.length === 0 && !loading && (
+ {filteredItems.length === 0 && !loading && (
@@ -231,7 +280,7 @@ export default function DiscrepancyDashboard() {
{loading && (
-
در حال بررسی و مقایسه اطلاعات...
+
در حال بررسی و استخراج اطلاعات...
)}
diff --git a/src/app/admin/locations/page.js b/src/app/admin/locations/page.js
index 3e0688f..f421df0 100644
--- a/src/app/admin/locations/page.js
+++ b/src/app/admin/locations/page.js
@@ -3,21 +3,26 @@ import { useState, useEffect } from 'react';
import Header from '@/components/Header';
import dynamic from 'next/dynamic';
import { motion, AnimatePresence } from 'framer-motion';
-import { ScanLine, Plus, Search, Trash2, Edit2, Layers, MapPin, X, AlertCircle, XCircle, Box } from 'lucide-react';
+import { ScanLine, Plus, Trash2, Edit2, Layers, MapPin, X, AlertCircle, XCircle, Box } from 'lucide-react';
const Scanner = dynamic(() => import('@yudiel/react-qr-scanner').then(mod => mod.Scanner), { ssr: false });
export default function AdminLocations() {
const [locations, setLocations] = useState([]);
const [warehouses, setWarehouses] = useState([]);
const [selectedWarehouse, setSelectedWarehouse] = useState('');
- const [newCode, setNewCode] = useState('');
+
+ // Separate states for location parts
+ const [floor, setFloor] = useState('');
+ const [region, setRegion] = useState('');
+ const [sector, setSector] = useState('');
+ const [row, setRow] = useState('');
+
const [loading, setLoading] = useState(false);
- const [cameraEnabled, setCameraEnabled] = useState(false);
+ const [cameraEnabled, setCameraEnabled] = useState(true);
const [camError, setCamError] = useState('');
const [activeFilter, setActiveFilter] = useState('all');
const [editingId, setEditingId] = useState(null);
- const [editCode, setEditCode] = useState('');
const [toast, setToast] = useState({ show: false, message: '', isError: false });
@@ -57,14 +62,37 @@ export default function AdminLocations() {
}
};
- const handleAddOrEdit = async (codeValue) => {
- const targetCode = editingId ? editCode : (codeValue || newCode);
- if (!targetCode) return;
+ // Smart Parser for the main code input
+ const handleFullCodeChange = (e) => {
+ const val = e.target.value.toUpperCase();
+
+ // Attempt to parse regex: [Letter][Number][Letter][Number] e.g. C2F3
+ const regex = /^([A-Za-z]+)(\d+)([A-Za-z]+)(\d+)$/;
+ const match = val.match(regex);
+
+ if (match) {
+ setFloor(match[1]);
+ setRegion(match[2]);
+ setSector(match[3]);
+ setRow(match[4]);
+ } else {
+ // If it doesn't match perfectly, just use it as floor or something, but best to let users type in separate fields
+ // We will just leave it empty or partially fill. Actually, it's better to clear them or let them type.
+ }
+ };
+
+ const handleAddOrEdit = async () => {
+ if (!floor || !region || !sector || !row) {
+ showToast('لطفا همه مقادیر طبقه، منطقه، قطاع و ردیف را وارد کنید', true);
+ return;
+ }
if (!selectedWarehouse) {
showToast('لطفاً ابتدا انبار را انتخاب کنید', true);
return;
}
+ const generatedCode = `${floor.toUpperCase()}${region}${sector.toUpperCase()}${row}`;
+
setLoading(true);
try {
const method = editingId ? 'PUT' : 'POST';
@@ -73,15 +101,14 @@ export default function AdminLocations() {
const res = await fetch(url, {
method,
headers: { 'Content-Type': 'application/json' },
- body: JSON.stringify({ code: targetCode, warehouse: selectedWarehouse })
+ body: JSON.stringify({ code: generatedCode, warehouse: selectedWarehouse })
});
const data = await res.json();
if (res.ok) {
showToast(editingId ? 'قفسه با موفقیت ویرایش شد' : 'قفسه با موفقیت ثبت شد!');
- setNewCode('');
+ setFloor(''); setRegion(''); setSector(''); setRow('');
setEditingId(null);
- setEditCode('');
fetchLocations();
} else {
showToast(data.error || 'خطا در ثبت قفسه', true);
@@ -112,8 +139,20 @@ export default function AdminLocations() {
const handleScan = (detectedCodes) => {
if (detectedCodes && detectedCodes.length > 0) {
- const scannedValue = detectedCodes[0].rawValue;
- handleAddOrEdit(scannedValue);
+ const scannedValue = detectedCodes[0].rawValue.toUpperCase();
+ const regex = /^([A-Za-z]+)(\d+)([A-Za-z]+)(\d+)$/;
+ const match = scannedValue.match(regex);
+ if (match) {
+ setFloor(match[1]);
+ setRegion(match[2]);
+ setSector(match[3]);
+ setRow(match[4]);
+ showToast(`کد ${scannedValue} شناسایی و پر شد`);
+ // Optional: auto-submit here if desired
+ // setTimeout(handleAddOrEdit, 500);
+ } else {
+ showToast(`کد اسکن شده نامعتبر است: ${scannedValue}`, true);
+ }
}
};
@@ -136,6 +175,8 @@ export default function AdminLocations() {
filteredLocations = filteredLocations.filter(loc => loc.floor === activeFilter);
}
+ const generatedCodePreview = (floor || region || sector || row) ? `${floor.toUpperCase()}${region}${sector.toUpperCase()}${row}` : '';
+
return (
@@ -148,7 +189,7 @@ export default function AdminLocations() {
{editingId ? 'ویرایش قفسه' : 'ثبت قفسه جدید'}
{editingId && (
-