refactor(mobile): more look and feel work
All checks were successful
Build and Push LST Docker Image / docker (push) Successful in 1m17s
All checks were successful
Build and Push LST Docker Image / docker (push) Successful in 1m17s
This commit is contained in:
@@ -1,6 +1,9 @@
|
||||
import { format } from "date-fns-tz";
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
import { Text, View } from "react-native";
|
||||
import { useAppStore } from "../hooks/useAppStore";
|
||||
import { useScannerStore } from "../hooks/useScannerStore";
|
||||
import { scannerFeedback } from "../lib/feedbackScan";
|
||||
import { sendTcpMessage } from "../lib/tcpScan";
|
||||
import { type ZebraScanResult, zebraScanner } from "../lib/ZebraScanner";
|
||||
import { ScannedLabelBox } from "./ScannedLabels";
|
||||
@@ -9,23 +12,32 @@ const STX = "\x02";
|
||||
const ETX = "\x03";
|
||||
|
||||
export default function ProdScanner() {
|
||||
const [lastScan, setLastScan] = useState<any>(null);
|
||||
const lastScan = useScannerStore((s) => s.lastScan);
|
||||
const setLastScan = useScannerStore((s) => s.setLastScan);
|
||||
const [tagScans, setTagScans] = useState<any>([]);
|
||||
const scannerIdFromStore = useAppStore((s) => s.scannerId);
|
||||
const serverIp = useAppStore((s) => s.serverIp);
|
||||
const serverPort = useAppStore((s) => s.serverPort);
|
||||
const [bgColor, setBGColor] = useState<string | null>(null);
|
||||
|
||||
const handleScan = useCallback(
|
||||
async (scan: ZebraScanResult) => {
|
||||
const scanned = scan.data;
|
||||
|
||||
let commandToSend = `${STX}${scannerIdFromStore}@${scanned}${ETX}`;
|
||||
let commandToSend = `${STX}${scannerIdFromStore}@${scan.data}${ETX}`;
|
||||
await scannerFeedback({
|
||||
type: "good",
|
||||
sound: true,
|
||||
vibrate: true,
|
||||
led: true,
|
||||
});
|
||||
|
||||
// if we are sscc we need to scan like this .... <STX>98@]C100090087710038712256<ETX>
|
||||
if (scan.data.startsWith("000")) {
|
||||
commandToSend = `${STX}${scannerIdFromStore}@]C1${scanned}${ETX}`;
|
||||
commandToSend = `${STX}${scannerIdFromStore}@]C1${scan.data}${ETX}`;
|
||||
setTagScans((prev: any) => [
|
||||
parseInt(scanned.slice(10, -1) || "000", 10).toString(),
|
||||
{
|
||||
label: parseInt(scan.data.slice(10, -1) || "000", 10).toString(),
|
||||
date: format(new Date(Date.now()), "HH:mm"),
|
||||
},
|
||||
...prev,
|
||||
]);
|
||||
}
|
||||
@@ -35,19 +47,44 @@ export default function ProdScanner() {
|
||||
setTagScans([]);
|
||||
}
|
||||
|
||||
const something = await sendTcpMessage(
|
||||
const scanned = (await sendTcpMessage(
|
||||
commandToSend,
|
||||
serverIp,
|
||||
parseInt(serverPort || "0", 10),
|
||||
);
|
||||
)) as any;
|
||||
// Later this is where your TCP send goes.
|
||||
// const response = await sendTcpMessage(tcpMessage);
|
||||
setLastScan(something.data[0]);
|
||||
console.log(scanned);
|
||||
await scannerFeedback({
|
||||
type: scanned.data[0]?.type === "error" ? "bad" : "good",
|
||||
sound: true,
|
||||
vibrate: true,
|
||||
led: true,
|
||||
});
|
||||
|
||||
if (scanned.data[0]?.type !== "error") {
|
||||
setBGColor("bg-green-500");
|
||||
setTimeout(() => {
|
||||
setBGColor(null);
|
||||
}, 1 * 1000);
|
||||
}
|
||||
|
||||
if (scanned.data[0]?.type === "error") {
|
||||
setBGColor("bg-red-500");
|
||||
setTimeout(() => {
|
||||
setBGColor(null);
|
||||
}, 1 * 1000);
|
||||
}
|
||||
setLastScan(scanned.data[0]);
|
||||
//console.log("TCP response:", something);
|
||||
},
|
||||
[scannerIdFromStore, serverIp, serverPort],
|
||||
[scannerIdFromStore, serverIp, serverPort, setLastScan],
|
||||
);
|
||||
|
||||
const clearScans = () => {
|
||||
setTagScans([]);
|
||||
};
|
||||
|
||||
console.log(lastScan);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -65,21 +102,17 @@ export default function ProdScanner() {
|
||||
};
|
||||
}, [handleScan]);
|
||||
return (
|
||||
<View>
|
||||
<View className={`${bgColor ?? ""} flex-1 w-screen`}>
|
||||
<View>
|
||||
<View style={{ alignItems: "center", margin: 10 }}>
|
||||
<Text style={{ fontSize: 20, fontWeight: "600" }}>
|
||||
<Text style={{ fontSize: 15, fontWeight: "600" }}>
|
||||
Scanner ID: {parseInt(scannerIdFromStore || "0", 10)}
|
||||
</Text>
|
||||
</View>
|
||||
{!lastScan ? (
|
||||
<View
|
||||
style={{
|
||||
marginTop: 10,
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
<Text className="text-xl font-bold">Waiting on scan....</Text>
|
||||
<View style={{ marginTop: 10, alignItems: "center" }}>
|
||||
<Text className="text-xl font-bold">Ready to scan</Text>
|
||||
<Text>Waiting for first scan...</Text>
|
||||
</View>
|
||||
) : (
|
||||
<View
|
||||
@@ -88,34 +121,25 @@ export default function ProdScanner() {
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
<Text style={{ fontSize: 20, fontWeight: "600" }}>
|
||||
{lastScan?.action}
|
||||
</Text>
|
||||
|
||||
{lastScan?.type === "error" ? (
|
||||
<View style={{ marginTop: 10, alignItems: "center" }}>
|
||||
<Text style={{ fontSize: 20, fontWeight: "600" }}>
|
||||
{lastScan?.message}
|
||||
{lastScan.action}
|
||||
</Text>
|
||||
) : (
|
||||
<View
|
||||
style={{
|
||||
marginTop: 15,
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
<Text style={{ fontSize: 20, fontWeight: "600" }}>
|
||||
{lastScan?.prompt}
|
||||
</Text>
|
||||
<Text style={{ fontSize: 20, fontWeight: "600" }}>
|
||||
{lastScan?.message}
|
||||
</Text>
|
||||
</View>
|
||||
)}
|
||||
|
||||
<Text style={{ fontSize: 20, fontWeight: "600" }}>
|
||||
{lastScan.message}
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
|
||||
<ScannedLabelBox labels={tagScans} />
|
||||
<View className="flex-1 w-full px-4">
|
||||
<ScannedLabelBox
|
||||
labels={tagScans}
|
||||
color={bgColor}
|
||||
clearScan={clearScans}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,50 +1,55 @@
|
||||
import { ScrollView, Text, View } from "react-native";
|
||||
import { ScrollView, View } from "react-native";
|
||||
import { SafeAreaView } from "react-native-safe-area-context";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Text } from "@/components/ui/text";
|
||||
|
||||
type ScannedLabel = {
|
||||
id: string;
|
||||
barcode: string;
|
||||
createdAt: string;
|
||||
label: string;
|
||||
date: Date;
|
||||
};
|
||||
|
||||
type ScannedLabelBoxProps = {
|
||||
labels: ScannedLabel[];
|
||||
color: string | null;
|
||||
clearScan: () => void;
|
||||
};
|
||||
|
||||
export function ScannedLabelBox({ labels }: ScannedLabelBoxProps) {
|
||||
export function ScannedLabelBox({
|
||||
labels,
|
||||
color,
|
||||
clearScan,
|
||||
}: ScannedLabelBoxProps) {
|
||||
return (
|
||||
<View style={{ flex: 1, marginTop: 30 }}>
|
||||
<Text style={{ fontSize: 18, fontWeight: "700", marginBottom: 8 }}>
|
||||
Current scanned labels
|
||||
</Text>
|
||||
<SafeAreaView className={`flex-1 w-full items-center ${color ?? ""}`}>
|
||||
<View className="flex flex-col gap-2">
|
||||
<Text style={{ fontSize: 18, fontWeight: "700", marginBottom: 8 }}>
|
||||
Current scanned labels
|
||||
</Text>
|
||||
</View>
|
||||
|
||||
<ScrollView
|
||||
style={{
|
||||
flex: 1,
|
||||
borderWidth: 1,
|
||||
borderColor: "#ccc",
|
||||
borderRadius: 8,
|
||||
padding: 2,
|
||||
margin: 2,
|
||||
}}
|
||||
contentContainerStyle={{ gap: 2 }}
|
||||
>
|
||||
<ScrollView className="w-full flex-1">
|
||||
{labels.length === 0 ? (
|
||||
<Text style={{ color: "#777" }}>No labels scanned yet</Text>
|
||||
<Text className="text-center">No labels scanned yet</Text>
|
||||
) : (
|
||||
labels.map((label) => (
|
||||
<View
|
||||
key={`${label}`}
|
||||
style={{
|
||||
padding: 2,
|
||||
borderRadius: 8,
|
||||
backgroundColor: "#f2f2f2",
|
||||
}}
|
||||
>
|
||||
<Text style={{ fontSize: 18, fontWeight: "700" }}>{label}</Text>
|
||||
</View>
|
||||
))
|
||||
<View className="flex items-center gap-2 w-full">
|
||||
{labels.map((i, index) => (
|
||||
<View
|
||||
key={`${i.label}-${index}`}
|
||||
className={`p-2 border rounded items-center ${color ?? ""}`}
|
||||
>
|
||||
<Text style={{ fontSize: 18, fontWeight: "700" }}>
|
||||
{i.label} - {i.date.toString()}
|
||||
</Text>
|
||||
</View>
|
||||
))}
|
||||
</View>
|
||||
)}
|
||||
</ScrollView>
|
||||
</View>
|
||||
{labels.length !== 0 && (
|
||||
<Button onPress={clearScan} variant="secondary">
|
||||
<Text>Clear Scans</Text>
|
||||
</Button>
|
||||
)}
|
||||
</SafeAreaView>
|
||||
);
|
||||
}
|
||||
|
||||
106
lstMobile/src/components/ui/button.tsx
Normal file
106
lstMobile/src/components/ui/button.tsx
Normal file
@@ -0,0 +1,106 @@
|
||||
import { TextClassContext } from '@/components/ui/text';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { cva, type VariantProps } from 'class-variance-authority';
|
||||
import { Platform, Pressable } from 'react-native';
|
||||
|
||||
const buttonVariants = cva(
|
||||
cn(
|
||||
'group shrink-0 flex-row items-center justify-center gap-2 rounded-md shadow-none',
|
||||
Platform.select({
|
||||
web: "focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive whitespace-nowrap outline-none transition-all focus-visible:ring-[3px] disabled:pointer-events-none [&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none [&_svg]:shrink-0",
|
||||
})
|
||||
),
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
default: cn(
|
||||
'bg-primary active:bg-primary/90 shadow-sm shadow-black/5',
|
||||
Platform.select({ web: 'hover:bg-primary/90' })
|
||||
),
|
||||
destructive: cn(
|
||||
'bg-destructive active:bg-destructive/90 dark:bg-destructive/60 shadow-sm shadow-black/5',
|
||||
Platform.select({
|
||||
web: 'hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40',
|
||||
})
|
||||
),
|
||||
outline: cn(
|
||||
'border-border bg-background active:bg-accent dark:bg-input/30 dark:border-input dark:active:bg-input/50 border shadow-sm shadow-black/5',
|
||||
Platform.select({
|
||||
web: 'hover:bg-accent dark:hover:bg-input/50',
|
||||
})
|
||||
),
|
||||
secondary: cn(
|
||||
'bg-secondary active:bg-secondary/80 shadow-sm shadow-black/5',
|
||||
Platform.select({ web: 'hover:bg-secondary/80' })
|
||||
),
|
||||
ghost: cn(
|
||||
'active:bg-accent dark:active:bg-accent/50',
|
||||
Platform.select({ web: 'hover:bg-accent dark:hover:bg-accent/50' })
|
||||
),
|
||||
link: '',
|
||||
},
|
||||
size: {
|
||||
default: cn('h-10 px-4 py-2 sm:h-9', Platform.select({ web: 'has-[>svg]:px-3' })),
|
||||
sm: cn('h-9 gap-1.5 rounded-md px-3 sm:h-8', Platform.select({ web: 'has-[>svg]:px-2.5' })),
|
||||
lg: cn('h-11 rounded-md px-6 sm:h-10', Platform.select({ web: 'has-[>svg]:px-4' })),
|
||||
icon: 'h-10 w-10 sm:h-9 sm:w-9',
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: 'default',
|
||||
size: 'default',
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
const buttonTextVariants = cva(
|
||||
cn(
|
||||
'text-foreground text-sm font-medium',
|
||||
Platform.select({ web: 'pointer-events-none transition-colors' })
|
||||
),
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
default: 'text-primary-foreground',
|
||||
destructive: 'text-white',
|
||||
outline: cn(
|
||||
'group-active:text-accent-foreground',
|
||||
Platform.select({ web: 'group-hover:text-accent-foreground' })
|
||||
),
|
||||
secondary: 'text-secondary-foreground',
|
||||
ghost: 'group-active:text-accent-foreground',
|
||||
link: cn(
|
||||
'text-primary group-active:underline',
|
||||
Platform.select({ web: 'underline-offset-4 hover:underline group-hover:underline' })
|
||||
),
|
||||
},
|
||||
size: {
|
||||
default: '',
|
||||
sm: '',
|
||||
lg: '',
|
||||
icon: '',
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: 'default',
|
||||
size: 'default',
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
type ButtonProps = React.ComponentProps<typeof Pressable> & VariantProps<typeof buttonVariants>;
|
||||
|
||||
function Button({ className, variant, size, ...props }: ButtonProps) {
|
||||
return (
|
||||
<TextClassContext.Provider value={buttonTextVariants({ variant, size })}>
|
||||
<Pressable
|
||||
className={cn(props.disabled && 'opacity-50', buttonVariants({ variant, size }), className)}
|
||||
role="button"
|
||||
{...props}
|
||||
/>
|
||||
</TextClassContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
export { Button, buttonTextVariants, buttonVariants };
|
||||
export type { ButtonProps };
|
||||
Reference in New Issue
Block a user