2026-03-30 18:00:14 +05:30
|
|
|
import { useCallback, useRef, useState, useEffect } from 'react'; // ← add useEffect
|
2026-01-27 18:27:29 +05:30
|
|
|
import { useDispatch } from 'react-redux';
|
|
|
|
|
import PozoWeightScaleIcon from './Pozo retail icons/PozoWeightScaleIcon';
|
|
|
|
|
import {
|
|
|
|
|
changeWeightScalePort,
|
|
|
|
|
changeWeightScaleWeight,
|
|
|
|
|
} from '../../../../Features/BookingScreen/BookingData/BookingData';
|
|
|
|
|
import WeightScaleComp from '../../../../Components/WeightScale/weightScale.jsx';
|
|
|
|
|
import { Messages } from '../../../../Components/Notifications/Messages';
|
|
|
|
|
import TooltipWrapper from '../../../../Components/Tooltip/Tooltip';
|
|
|
|
|
import { isMobile } from 'react-device-detect';
|
2026-03-30 18:00:14 +05:30
|
|
|
import { Capacitor } from '@capacitor/core';
|
|
|
|
|
import { weightScaleService } from '../../../../Services/getusbdevices.js';
|
2026-01-27 18:27:29 +05:30
|
|
|
|
|
|
|
|
const BSNavBarWeightScale = ({Combo}) => {
|
|
|
|
|
const dispatch = useDispatch();
|
2026-03-30 18:00:14 +05:30
|
|
|
|
|
|
|
|
// ── FIX: init from service so remount restores correct status ──
|
|
|
|
|
const [usbStatus, setUsbStatus] = useState(
|
|
|
|
|
() => weightScaleService.isConnected ? 'connected' : 'disconnected'
|
|
|
|
|
);
|
|
|
|
|
|
2026-01-27 18:27:29 +05:30
|
|
|
const [WeightScale, setWeightScale] = useState(false);
|
|
|
|
|
const [WeightScale2, setWeightScale2] = useState(false);
|
2026-03-30 18:00:14 +05:30
|
|
|
const [wsport, setwsport] = useState(null);
|
|
|
|
|
|
2026-01-27 18:27:29 +05:30
|
|
|
const [messageType, setMessageType] = useState(null);
|
|
|
|
|
const [messageData, setMessageData] = useState(null);
|
2026-03-30 18:00:14 +05:30
|
|
|
|
|
|
|
|
const lastStableWeightRef = useRef(null);
|
|
|
|
|
const lastWeightRef = useRef(null);
|
|
|
|
|
const stableCountRef = useRef(0);
|
|
|
|
|
const STABLE_THRESHOLD = 3;
|
|
|
|
|
|
|
|
|
|
const isNativeApp = Capacitor.isNativePlatform();
|
|
|
|
|
|
|
|
|
|
// ── FIX: on remount, if already connected re-attach the reading callback ──
|
2026-01-27 18:27:29 +05:30
|
|
|
useEffect(() => {
|
2026-03-30 18:00:14 +05:30
|
|
|
if (weightScaleService.isConnected) {
|
|
|
|
|
setUsbStatus('connected');
|
|
|
|
|
dispatch(changeWeightScalePort(true));
|
|
|
|
|
|
|
|
|
|
// Re-register the weight reading callback since component remounted
|
|
|
|
|
weightScaleService.startReading(
|
|
|
|
|
(w) => handleWeightReading(w),
|
|
|
|
|
(err) => {
|
|
|
|
|
setUsbStatus('disconnected');
|
|
|
|
|
dispatch(changeWeightScalePort(false));
|
|
|
|
|
dispatch(changeWeightScaleWeight(null));
|
|
|
|
|
resetStable();
|
|
|
|
|
setMessageType('error');
|
|
|
|
|
setMessageData('Scale error: ' + err.message);
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ── FIX: on unmount, do NOT disconnect — just clear the reading callback
|
|
|
|
|
// so old callbacks don't fire on the unmounted component (memory leak)
|
|
|
|
|
return () => {
|
|
|
|
|
weightScaleService.detachCallbacks();
|
|
|
|
|
};
|
|
|
|
|
}, []); // runs once on mount/remount
|
|
|
|
|
|
|
|
|
|
// ── ICON COLOR ────────────────────────────────────
|
|
|
|
|
const getIconColor = () => {
|
|
|
|
|
if (isNativeApp || isMobile) {
|
|
|
|
|
if (usbStatus === 'connected') return 'rgb(82, 196, 26)';
|
|
|
|
|
if (usbStatus === 'connecting') return '#faad14';
|
|
|
|
|
return '#ff4d4f';
|
|
|
|
|
}
|
|
|
|
|
return WeightScale2 ? 'rgb(82, 196, 26)' : '#ff4d4f';
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// ── STABLE WEIGHT DETECTION ───────────────────────
|
|
|
|
|
const handleWeightReading = (w) => {
|
|
|
|
|
if (w === lastWeightRef.current) {
|
|
|
|
|
stableCountRef.current++;
|
2026-01-27 18:27:29 +05:30
|
|
|
} else {
|
2026-03-30 18:00:14 +05:30
|
|
|
stableCountRef.current = 1;
|
2026-01-27 18:27:29 +05:30
|
|
|
}
|
2026-03-30 18:00:14 +05:30
|
|
|
lastWeightRef.current = w;
|
|
|
|
|
|
|
|
|
|
const isStable = stableCountRef.current >= STABLE_THRESHOLD;
|
|
|
|
|
const isValid = typeof w === 'number' && !isNaN(w);
|
|
|
|
|
const hasChanged = w !== lastStableWeightRef.current;
|
|
|
|
|
|
|
|
|
|
if (isValid && isStable && hasChanged) {
|
|
|
|
|
lastStableWeightRef.current = w;
|
|
|
|
|
dispatch(changeWeightScaleWeight(w));
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const resetStable = () => {
|
|
|
|
|
lastStableWeightRef.current = null;
|
|
|
|
|
lastWeightRef.current = null;
|
|
|
|
|
stableCountRef.current = 0;
|
2026-01-27 18:27:29 +05:30
|
|
|
};
|
2026-03-30 18:00:14 +05:30
|
|
|
|
|
|
|
|
// ── CONNECT ───────────────────────────────────────
|
|
|
|
|
const connectScale = async () => {
|
|
|
|
|
setUsbStatus('connecting');
|
|
|
|
|
resetStable();
|
|
|
|
|
|
|
|
|
|
const success = await weightScaleService.autoConnect();
|
|
|
|
|
|
|
|
|
|
if (success) {
|
|
|
|
|
setUsbStatus('connected');
|
|
|
|
|
dispatch(changeWeightScalePort(true));
|
|
|
|
|
|
|
|
|
|
await weightScaleService.startReading(
|
|
|
|
|
(w) => handleWeightReading(w),
|
|
|
|
|
(err) => {
|
|
|
|
|
setUsbStatus('disconnected');
|
|
|
|
|
dispatch(changeWeightScalePort(false));
|
|
|
|
|
dispatch(changeWeightScaleWeight(null));
|
|
|
|
|
resetStable();
|
|
|
|
|
setMessageType('error');
|
|
|
|
|
setMessageData('Scale error: ' + err.message);
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
} else {
|
|
|
|
|
setUsbStatus('disconnected');
|
|
|
|
|
setMessageType('error');
|
|
|
|
|
setMessageData(weightScaleService.lastError || 'Cannot connect. Check USB / OTG cable.');
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// ── DISCONNECT ────────────────────────────────────
|
|
|
|
|
const disconnectScale = async () => {
|
|
|
|
|
await weightScaleService.disconnect();
|
|
|
|
|
setUsbStatus('disconnected');
|
|
|
|
|
dispatch(changeWeightScalePort(false));
|
|
|
|
|
dispatch(changeWeightScaleWeight(null));
|
|
|
|
|
resetStable();
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// ── MAIN CLICK HANDLER ────────────────────────────
|
|
|
|
|
const handleWeightScaleOpen = async () => {
|
|
|
|
|
if (isNativeApp || isMobile) {
|
|
|
|
|
if (usbStatus === 'connected') {
|
|
|
|
|
await disconnectScale();
|
|
|
|
|
} else if (usbStatus === 'disconnected') {
|
|
|
|
|
await connectScale();
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
const next = !WeightScale2;
|
|
|
|
|
setWeightScale2(next);
|
|
|
|
|
setWeightScale(next);
|
|
|
|
|
dispatch(changeWeightScalePort(next));
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// ── DESKTOP HANDLERS ──────────────────────────────
|
2026-01-27 18:27:29 +05:30
|
|
|
const setWsport = async (e) => {
|
|
|
|
|
if (e?.readable) {
|
|
|
|
|
setwsport(e);
|
|
|
|
|
} else {
|
|
|
|
|
setMessageType('error');
|
|
|
|
|
setMessageData('Please check weightscale connection');
|
|
|
|
|
setWeightScale2(false);
|
2026-03-30 18:00:14 +05:30
|
|
|
setWeightScale(false);
|
2026-01-27 18:27:29 +05:30
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const myfun = async (e) => {
|
|
|
|
|
dispatch(changeWeightScaleWeight(e));
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const onComplete = useCallback(() => {
|
|
|
|
|
setMessageData(null);
|
|
|
|
|
setMessageType(null);
|
|
|
|
|
}, []);
|
2026-03-30 18:00:14 +05:30
|
|
|
|
2026-01-27 18:27:29 +05:30
|
|
|
const msgAlert = async (e) => {
|
|
|
|
|
setMessageType(e.type);
|
|
|
|
|
setMessageData(e.msg);
|
|
|
|
|
};
|
2026-03-30 18:00:14 +05:30
|
|
|
|
2026-01-27 18:27:29 +05:30
|
|
|
return (
|
|
|
|
|
<div className="BSBilling7-icon-flex">
|
|
|
|
|
<Messages
|
|
|
|
|
messageType={messageType}
|
|
|
|
|
messageData={messageData}
|
|
|
|
|
onComplete={onComplete}
|
|
|
|
|
/>
|
|
|
|
|
<TooltipWrapper title={'Weight-Scale'} isMobile={isMobile}>
|
2026-03-30 18:00:14 +05:30
|
|
|
{Combo == 'combolayout' ? (
|
2026-01-27 18:27:29 +05:30
|
|
|
<button
|
2026-03-30 18:00:14 +05:30
|
|
|
className="BSC1Search-button1"
|
|
|
|
|
style={{ backgroundColor: getIconColor() }}
|
|
|
|
|
onClick={handleWeightScaleOpen}
|
|
|
|
|
disabled={usbStatus === 'connecting'}
|
|
|
|
|
>
|
|
|
|
|
{(isNativeApp || isMobile)
|
|
|
|
|
? usbStatus === 'connecting' ? 'Connecting...'
|
|
|
|
|
: usbStatus === 'connected' ? 'Scale ON'
|
|
|
|
|
: 'Weight Scale'
|
|
|
|
|
: 'Weight Scale'}
|
|
|
|
|
</button>
|
|
|
|
|
) : (
|
|
|
|
|
<PozoWeightScaleIcon
|
|
|
|
|
fill={getIconColor()}
|
|
|
|
|
className="BSBillingNav-icon"
|
|
|
|
|
onClick={handleWeightScaleOpen}
|
|
|
|
|
/>
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
{WeightScale && !isMobile && !isNativeApp && (
|
|
|
|
|
<WeightScaleComp
|
|
|
|
|
testfun={myfun}
|
|
|
|
|
setportfun={setWsport}
|
|
|
|
|
portval={wsport}
|
|
|
|
|
portclose={WeightScale2}
|
|
|
|
|
msg={msgAlert}
|
|
|
|
|
/>
|
|
|
|
|
)}
|
2026-01-27 18:27:29 +05:30
|
|
|
</TooltipWrapper>
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
};
|
2026-03-30 18:00:14 +05:30
|
|
|
|
|
|
|
|
export default BSNavBarWeightScale;
|