diff --git a/backend/db/schema/scanUsers.ts b/backend/db/schema/scanUsers.ts index c4ac877..74589ac 100644 --- a/backend/db/schema/scanUsers.ts +++ b/backend/db/schema/scanUsers.ts @@ -1,5 +1,6 @@ import { boolean, + jsonb, pgEnum, pgTable, text, @@ -25,7 +26,7 @@ export const scanUser = pgTable( scannerId: text("scanner_id").unique().notNull(), pinNumber: text("pin_number").unique().notNull(), pinHash: text("pin_hash").notNull(), - excludedCommand: text("excluded_commands").default(""), + excludedCommand: jsonb("excluded_commands").default([]), role: mobileRoleEnum("role").notNull().default("user"), active: boolean("active").default(true), lastScan: timestamp("last_scan").defaultNow(), diff --git a/backend/db/schema/scanlog.schema.ts b/backend/db/schema/scanlog.schema.ts index c17e0cb..721557e 100644 --- a/backend/db/schema/scanlog.schema.ts +++ b/backend/db/schema/scanlog.schema.ts @@ -4,6 +4,7 @@ import type z from "zod"; export const scanLog = pgTable("scan_log", { id: uuid("id").defaultRandom().primaryKey(), + user: text("user"), scannerId: text("scanner_id"), message: text("message").notNull(), prompt: text("prompt"), diff --git a/lstMobile/app.json b/lstMobile/app.json index ef960fa..42d4d41 100644 --- a/lstMobile/app.json +++ b/lstMobile/app.json @@ -15,7 +15,7 @@ "foregroundImage": "./assets/adaptive-icon-white.png", "backgroundColor": "#ffffff" }, - "versionCode": 23, + "versionCode": 24, "minSupportedVersionCode": 21, "predictiveBackGestureEnabled": false, "package": "net.alpla.lst.mobile" diff --git a/lstMobile/assets/sounds/scan.wav b/lstMobile/assets/sounds/scan.wav new file mode 100644 index 0000000..015e1f6 Binary files /dev/null and b/lstMobile/assets/sounds/scan.wav differ diff --git a/lstMobile/src/app/(tabs)/_layout.tsx b/lstMobile/src/app/(tabs)/_layout.tsx index 63d1005..bcdeb4f 100644 --- a/lstMobile/src/app/(tabs)/_layout.tsx +++ b/lstMobile/src/app/(tabs)/_layout.tsx @@ -1,9 +1,32 @@ -import { Tabs } from "expo-router"; -import { Home, Settings } from "lucide-react-native"; +import { Redirect, Tabs } from "expo-router"; +import { Container, Home, Logs, Rows4, Settings } from "lucide-react-native"; import { useAppStore } from "../../hooks/useAppStore"; +import { useMobileAuthStore } from "../../hooks/useMobileAuth"; + +// const roles = { +// adminOnly: ["admin"], +// management: ["admin", "manager"], +// allStaff: ["admin", "manager", "driver", "lead", "user"], +// }; export default function TabsLayout() { const serverPort = useAppStore((s) => s.serverPort); + const user = useMobileAuthStore((s) => s.user); + const isUnlocked = useMobileAuthStore((s) => s.isUnlocked); + + const port = parseInt(serverPort || "0", 10) >= 50000; + + if (!user || (!isUnlocked && !port)) { + return ; + } + + const isNormalScanner = parseInt(serverPort || "0", 10) >= 50000; + + const hasRole = (allowed: string[] = []) => { + const role = user?.role?.toLowerCase(); + return role ? allowed.includes(role) : false; + }; + return ( , + }} + /> + ( - + ), }} /> @@ -40,7 +76,10 @@ export default function TabsLayout() { options={{ title: "Logs", href: - parseInt(serverPort || "0", 10) >= 50000 ? null : "/(tabs)/logs", + isNormalScanner || !hasRole(["admin", "manager"]) + ? null + : "/(tabs)/logs", + tabBarIcon: ({ color, size }) => , }} /> {/* = 50000 ? null : "/(tabs)/logs", }} /> */} + ( + + ), + }} + /> ); } diff --git a/lstMobile/src/app/(tabs)/config.tsx b/lstMobile/src/app/(tabs)/config.tsx index 0a505f5..4b93f5d 100644 --- a/lstMobile/src/app/(tabs)/config.tsx +++ b/lstMobile/src/app/(tabs)/config.tsx @@ -1,7 +1,5 @@ -import { Link } from "expo-router"; -import { Text, View } from "react-native"; import Setup from "../setup"; export default function SettingsTab() { - return -} \ No newline at end of file + return ; +} diff --git a/lstMobile/src/app/(tabs)/dockScan.tsx b/lstMobile/src/app/(tabs)/dockScan.tsx new file mode 100644 index 0000000..690ae5f --- /dev/null +++ b/lstMobile/src/app/(tabs)/dockScan.tsx @@ -0,0 +1,26 @@ +import React from "react"; +import { Text, View } from "react-native"; +import { Button } from "../../components/ui/button"; + +export default function LaneCheck() { + const getInfo = async () => { + const info = "ho"; + + console.log(info); + }; + return ( + + Dock Scanning + + + ); +} diff --git a/lstMobile/src/app/(tabs)/laneCheck.tsx b/lstMobile/src/app/(tabs)/laneCheck.tsx new file mode 100644 index 0000000..60fca05 --- /dev/null +++ b/lstMobile/src/app/(tabs)/laneCheck.tsx @@ -0,0 +1,37 @@ +import React, { useCallback, useEffect } from "react"; +import { Text, View } from "react-native"; +import { Button } from "../../components/ui/button"; +import { type ZebraScanResult, zebraScanner } from "../../lib/ZebraScanner"; + +export default function LaneCheck() { + const handleScan = useCallback(async (scan: ZebraScanResult) => { + console.log(scan); + }, []); + + useEffect(() => { + zebraScanner.ensureProfile(); + zebraScanner.startListening(); + + const sub = zebraScanner.addScanListener((scan) => { + //console.log("SCAN:", scan); + handleScan(scan); + }); + + return () => { + sub.remove(); + zebraScanner.stopListening(); + }; + }, [handleScan]); + return ( + + LaneChecks + + ); +} diff --git a/lstMobile/src/app/_layout.tsx b/lstMobile/src/app/_layout.tsx index f9691d5..3aed40d 100644 --- a/lstMobile/src/app/_layout.tsx +++ b/lstMobile/src/app/_layout.tsx @@ -1,26 +1,21 @@ +import { PortalHost } from "@rn-primitives/portal"; import { Stack } from "expo-router"; import { StatusBar } from "expo-status-bar"; import "../../global.css"; -import { PortalHost } from "@rn-primitives/portal"; -import { View } from "react-native"; +import useDeviceLock from "../hooks/useDeviceCheck"; export default function RootLayout() { + useDeviceLock(); + return ( <> - - - + + + + diff --git a/lstMobile/src/app/index.tsx b/lstMobile/src/app/index.tsx index 8bd0236..a1f43b8 100644 --- a/lstMobile/src/app/index.tsx +++ b/lstMobile/src/app/index.tsx @@ -1,127 +1,31 @@ -import axios from "axios"; -import Constants from "expo-constants"; -import { Redirect, useRouter } from "expo-router"; -import { useEffect, useState } from "react"; +import { Redirect } from "expo-router"; import { ActivityIndicator, Text, View } from "react-native"; -import { useAppStore } from "../hooks/useAppStore"; -import { useMobileAuthStore } from "../hooks/useMobileAuth"; -import { useServerStore } from "../hooks/useServerCheck"; -import { devDelay } from "../lib/devMode"; +import { useAppStartup } from "../hooks/useAppStartup"; + +const startupMessages = { + loading: "Loading app...", + validating: "Validating data...", + scannerMode: "Checking scanner mode...", + normalScanner: "Starting normal ALPLAprod scanner that has no LST rules", + checkingUpdates: "Checking for updates...", + opening: "Opening LST scan app...", + error: "Something went wrong during startup.", +}; export default function Index() { - const router = useRouter(); - const [message, setMessage] = useState(Starting app...); - const [ready, setReady] = useState(false); - const setServerVersion = useServerStore((s) => s.setServerVersion); - //const { isUnlocked } = useMobileAuthStore(); + const { ready, startupRoute, status } = useAppStartup(); - const hasHydrated = useAppStore((s) => s.hasHydrated); - const serverPort = useAppStore((s) => s.serverPort); - const serverIp = useAppStore((s) => s.serverIp); - const build = Constants.expoConfig?.android?.versionCode ?? 1; - const hasValidSetup = useAppStore((s) => s.hasValidSetup); - - useEffect(() => { - if (!hasHydrated) { - setMessage(Loading app...); - return; - } - - const startup = async () => { - try { - await devDelay(1500); - - setMessage(Validating data...); - await devDelay(1500); - - if (!hasValidSetup()) { - router.replace("/setup"); - return; - } - - // checking for lst. - console.log( - `http://${serverIp}:${parseInt(serverPort || "0", 10) >= 50000 ? "3000" : serverPort}/lst/api/mobile/version`, - ); - try { - const res = await axios.get( - `http://${serverIp}:${parseInt(serverPort || "0", 10) >= 50000 ? "3000" : serverPort}/lst/api/mobile/version`, - { - timeout: 5000, - }, - ); - - console.log(res.data); - - // if the build version dose not match the latest server version force update - if (res.status === 200) { - setServerVersion(res.data); - } - - // TODO: change the header to show orange and theres a new version - // console.log(build < res.data.minSupportedVersionCode); - // if (build < res.data.minSupportedVersionCode) { - // router.replace("/updateScreen"); - // return; - // } - } catch (error) { - console.log("Error: ", error); - } - - setMessage(Checking scanner mode...); - await devDelay(1500); - - if (parseInt(serverPort || "0", 10) >= 50000) { - setMessage( - - Starting normal alplaprod scanner that has no LST rules - , - ); - await devDelay(1500); - //router.replace("/scanner"); - setReady(true); - return; - } - - setMessage(Checking for updates); - await devDelay(1500); - // TODO if theres an update go to update screen message :D - setMessage(Opening LST scan app); - await devDelay(3250); - - setReady(true); - } catch (error) { - console.log("Startup error", error); - setMessage(Something went wrong during startup.); - } - }; - - startup(); - }, [ - hasHydrated, - hasValidSetup, - serverPort, - serverIp, - router, - setServerVersion, - ]); - - // if (ready && !isUnlocked) { - // return ; - // } - if (ready) { - return ; + if (ready && startupRoute) { + return ; } + + if (ready) { + return ; + } + return ( - - {message} + + {startupMessages[status]} ); diff --git a/lstMobile/src/app/login.tsx b/lstMobile/src/app/login.tsx index 18f3acc..a4dd642 100644 --- a/lstMobile/src/app/login.tsx +++ b/lstMobile/src/app/login.tsx @@ -1,27 +1,49 @@ import axios from "axios"; -import Constants from "expo-constants"; + import { useRouter } from "expo-router"; import { useState } from "react"; -import { Alert, Button, Text, View } from "react-native"; +import { Button, Text, View } from "react-native"; import { Input } from "../components/ui/input"; import { useAppStore } from "../hooks/useAppStore"; import { useMobileAuthStore } from "../hooks/useMobileAuth"; export default function Login() { - const { setUser } = useMobileAuthStore(); + // doing this causes rerender and sub + //const { setUser } = useMobileAuthStore(); + const [pin, setPin] = useState(""); const serverPort = useAppStore((s) => s.serverPort); const serverIp = useAppStore((s) => s.serverIp); + + const router = useRouter(); + const onLogin = async () => { + if (pin.length < 6) { + console.log("pin must be min 6 "); + } + console.log(pin); try { - const res = await axios.get( - `http://${serverIp}:${parseInt(serverPort || "0", 10) >= 50000 ? "3000" : serverPort}/lst/api/mobile/version`, + const res = await axios.post( + `http://${serverIp}:${parseInt(serverPort || "0", 10) >= 50000 ? "3000" : serverPort}/lst/api/mobile/auth/pin`, + { pin }, + { timeout: 5000, }, ); - console.log(res.data); - } catch (error) {} + if (res.status === 200) { + // this way to set the user is direct and basically a 1 shot + useMobileAuthStore.getState().setUser(res.data.data); + return router.replace("/(tabs)/scanner"); + } + } catch (error) { + console.log(error); + } + }; + + const config = () => { + console.log("config"); + return router.replace("/setup"); }; return ( @@ -43,10 +65,21 @@ export default function Login() { keyboardType="number-pad" textContentType="oneTimeCode" placeholder="Pin number" + onChangeText={setPin} /> -