diff --git a/src/components/displays/department/pos/steps/mobile/PosDepartmentStepMobile1.vue b/src/components/displays/department/pos/steps/mobile/PosDepartmentStepMobile1.vue index 0106080c..fb8b4e6b 100644 --- a/src/components/displays/department/pos/steps/mobile/PosDepartmentStepMobile1.vue +++ b/src/components/displays/department/pos/steps/mobile/PosDepartmentStepMobile1.vue @@ -13,7 +13,7 @@ import PosDepartmentStep1MobileManualInput from "@/components/displays/department/pos/steps/mobile/views/PosDepartmentStep1MobileManualInput.vue"; import PosDepartmentStepMobileButtonNextStep from "@/components/displays/department/pos/steps/mobile/elements/PosDepartmentStepMobileButtonNextStep.vue"; -import { manualInput, vehicles, camera } from "./objects/PosDepartmentStepMobileFlow.vue"; +import { manualInput, vehicles, camera, sounds } from "./objects/PosDepartmentStepMobileFlow.vue"; import { PosSearchResult } from "./objects/PosSearchResult.vue"; import RegistrationNumberSearchResult from "@/components/models/pos/step1/RegistrationNumberSearchResult.vue"; import UnknownCustomer from "@/components/viewport/elements/icons/UnknownCustomer.vue"; @@ -54,6 +54,10 @@ const handleLPRResult = () => { customerStatus: 'unknown' }); if (searchResultIndex === vehicles.activeVehicleIndex.value) { + // Only play the sound if the vehicle registration number is being set / changed. + if (vehicles.getActiveVehicle()?.reg !== latestLPRResponse.value?.license_plate_number) { + sounds.play(sounds.list.value.onAfterSuccessfulScan) + } vehicles.select(searchResultIndex, { reg: latestLPRResponse.value?.license_plate_number || "", status: 'unknown' @@ -63,6 +67,11 @@ const handleLPRResult = () => { const parseImage = (image: string) => { + // Check if the time since the last successful parse is enough + if (!camera.hasDelayAfterSuccessPassed()) { + console.log("Skipping parsing due to delay after success."); + return; + } if (lastParsedImage.value === image) { // If the image is the same as the last parsed one, skip parsing console.log("Skipping parsing for the same image."); @@ -79,6 +88,8 @@ const parseImage = (image: string) => { ).then((response) => { latestLPRResponse.value = response.data.data as LPRResponse; console.log("LPR Response:", latestLPRResponse.value); + // Set the last successful capture time + camera.setLastSuccess(); // Handle parsed result. handleLPRResult() diff --git a/src/components/displays/department/pos/steps/mobile/elements/PosDepartmentStepMobile1Location.vue b/src/components/displays/department/pos/steps/mobile/elements/PosDepartmentStepMobile1Location.vue index 260730cf..2ae38800 100644 --- a/src/components/displays/department/pos/steps/mobile/elements/PosDepartmentStepMobile1Location.vue +++ b/src/components/displays/department/pos/steps/mobile/elements/PosDepartmentStepMobile1Location.vue @@ -26,17 +26,19 @@ watch(coords, (newCoords) => { diff --git a/src/components/displays/department/pos/steps/mobile/objects/PosDepartmentStepMobileFlow.vue b/src/components/displays/department/pos/steps/mobile/objects/PosDepartmentStepMobileFlow.vue index 201c2bfa..470e0bef 100644 --- a/src/components/displays/department/pos/steps/mobile/objects/PosDepartmentStepMobileFlow.vue +++ b/src/components/displays/department/pos/steps/mobile/objects/PosDepartmentStepMobileFlow.vue @@ -459,8 +459,33 @@ const productList = { // Define the reactive properties const latestImage = ref(null); const isCameraMounted = ref(false); -const cameraImageCaptureDelayInitial = ref(3000); // Initial delay for camera image capture in milliseconds (First capture) -const cameraImageCaptureDelaySubsequent = ref(10000); // Further captures delay in milliseconds (Every capture after the first one) +const cameraImageCaptureDelayInitial = ref(2000); // Initial delay for camera image capture in milliseconds (First capture) +const cameraImageCaptureDelaySubsequent = ref(1000); // Further captures delay in milliseconds (Every capture after the first one) +const cameraImageCaptureDelayAfterSuccess = ref(5000); // Delay after a successful capture in milliseconds (After a successful capture, before the next one) +const cameraImageCaptureLastSuccess = ref(null); // Timestamp of the last successful capture + +// Function to set the timestamp of the last successful capture +const setCameraImageCaptureLastSuccess = (timestamp?: number | null) => { + cameraImageCaptureLastSuccess.value = ( timestamp !== undefined ? timestamp : Date.now() ); +} +// Function to get the timestamp of the last successful capture +const getCameraImageCaptureLastSuccess = () => { + return cameraImageCaptureLastSuccess.value; +} +// Function to get the delay after a successful capture +const getCameraImageCaptureDelayAfterSuccess = () => { + return cameraImageCaptureDelayAfterSuccess.value; +} +// Function to set the delay after a successful capture +const setCameraImageCaptureDelayAfterSuccess = (delay: number) => { + cameraImageCaptureDelayAfterSuccess.value = delay; +} +// Function to check if the delay after a successful capture has passed +const hasCameraImageCaptureDelayAfterSuccessPassed = (): boolean => { + if (cameraImageCaptureLastSuccess.value === null) return true; // No successful capture yet + const currentTime = Date.now(); + return (currentTime - cameraImageCaptureLastSuccess.value) > cameraImageCaptureDelayAfterSuccess.value; +} // Function to retrieve the latest image frame. const getLatestImage = async () => { return latestImage.value; @@ -503,7 +528,13 @@ const camera = { clearLatestImage, clearMounted: clearCameraMounted, getImageCaptureDelay: getCameraImageCaptureDelay, - setImageCaptureDelay: setCameraImageCaptureDelay + setImageCaptureDelay: setCameraImageCaptureDelay, + // Capture delay after success functions + getImageCaptureDelayAfterSuccess: getCameraImageCaptureDelayAfterSuccess, + setImageCaptureDelayAfterSuccess: setCameraImageCaptureDelayAfterSuccess, + setLastSuccess: setCameraImageCaptureLastSuccess, + getLastSuccess: getCameraImageCaptureLastSuccess, + hasDelayAfterSuccessPassed: hasCameraImageCaptureDelayAfterSuccessPassed, } /** Metadata */ diff --git a/src/components/forms/auth/LoginForm.vue b/src/components/forms/auth/LoginForm.vue index 940f8a69..4c474152 100644 --- a/src/components/forms/auth/LoginForm.vue +++ b/src/components/forms/auth/LoginForm.vue @@ -94,7 +94,7 @@ SessionUser.auth.reCAPTCHA.preCheck.get().then((response) => {
Login - + diff --git a/src/views/auth/LoginQR.vue b/src/views/auth/LoginQR.vue index 193d83ff..0d781fdf 100644 --- a/src/views/auth/LoginQR.vue +++ b/src/views/auth/LoginQR.vue @@ -9,6 +9,7 @@ import PosDepartmentStepMobileFixedBottomControl import { QrcodeStream, QrcodeDropZone, QrcodeCapture } from 'vue-qrcode-reader' import { sounds } from "@/components/displays/department/pos/steps/mobile/objects/PosDepartmentStepMobileFlow.vue"; import PageLoader from "@/components/global/PageLoader.vue"; +import Swal from "sweetalert2"; const isProcessingQRCode = ref(false); // State to indicate if QR code is being processed const playCaptureSound = () => { @@ -52,6 +53,11 @@ const isValidUrl = (string: string) => { // Check if the url is from the same origin if (!string.startsWith(window.location.origin)) { console.log("URL is not from the same origin:", string, window.location.origin); + Swal.fire({ + icon: 'error', + title: 'Untrusted QR Code', + text: 'The scanned QR code is not from a trusted source.', + }); return false; } // Check if the url contains the token parameter diff --git a/src/views/guest/pwa/PWADownload.vue b/src/views/guest/pwa/PWADownload.vue index b61f3f78..11b7f175 100644 --- a/src/views/guest/pwa/PWADownload.vue +++ b/src/views/guest/pwa/PWADownload.vue @@ -113,7 +113,6 @@ onUnmounted(() => { Pleno is installed Install Pleno Install Pleno through your browser menu - Oh well