Login: fixed-length 4-digit PIN that auto-submits on the 4th digit (no submit button to scroll to on small iPhone screens) and clears on a wrong PIN. Compact, vertically-centered keypad so it fits without scrolling. Operator tracking: after PIN auth, staff enter their name (new /operator screen, persisted per device). The name is sent as X-Operator on every authed request and recorded on each check-in/undo/ice audit entry (new Operator column), so logs show who did what. Shown in the scanner header and the admin audit view. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
59 lines
1.6 KiB
TypeScript
59 lines
1.6 KiB
TypeScript
import { useEffect } from "react";
|
|
import { View, ActivityIndicator } from "react-native";
|
|
import { Stack, useRouter, useSegments } from "expo-router";
|
|
import { SafeAreaProvider } from "react-native-safe-area-context";
|
|
import { StatusBar } from "expo-status-bar";
|
|
import { AuthProvider, useAuth } from "../lib/auth";
|
|
import { theme } from "../lib/theme";
|
|
|
|
export default function RootLayout() {
|
|
return (
|
|
<SafeAreaProvider>
|
|
<StatusBar style="light" />
|
|
<AuthProvider>
|
|
<AuthGate />
|
|
</AuthProvider>
|
|
</SafeAreaProvider>
|
|
);
|
|
}
|
|
|
|
function AuthGate() {
|
|
const { ready, signedIn, operator } = useAuth();
|
|
const router = useRouter();
|
|
const segments = useSegments();
|
|
|
|
useEffect(() => {
|
|
if (!ready) return;
|
|
const route = segments[0];
|
|
const onLogin = route === "login";
|
|
const onOperator = route === "operator";
|
|
if (!signedIn) {
|
|
if (!onLogin) router.replace("/login");
|
|
return;
|
|
}
|
|
// Signed in via PIN — require an operator name before using the app.
|
|
if (!operator) {
|
|
if (!onOperator) router.replace("/operator");
|
|
return;
|
|
}
|
|
if (onLogin || onOperator) router.replace("/");
|
|
}, [ready, signedIn, operator, segments, router]);
|
|
|
|
if (!ready) {
|
|
return (
|
|
<View style={{ flex: 1, backgroundColor: theme.bg, alignItems: "center", justifyContent: "center" }}>
|
|
<ActivityIndicator color={theme.successBright} size="large" />
|
|
</View>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<Stack
|
|
screenOptions={{
|
|
headerShown: false,
|
|
contentStyle: { backgroundColor: theme.bg },
|
|
animation: "fade",
|
|
}}
|
|
/>
|
|
);
|
|
}
|