Add request logging mode, dark mode, Account hub, profile, vehicle CRUD, cards view

- Client: logRequests option + redacted request/response logging (setLogRequests to
  toggle at runtime); enabled via app extra.debugHttp for on-device 401 debugging
- Dark mode (persisted) via ThemeProvider + React Navigation theme
- New "Account" tab: Profile (view), Vehicles (full CRUD), Payment methods (view-only),
  About, dark-mode + request-logging toggles, sign out

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Hank 2026-07-06 10:14:24 -07:00
parent 65e9118806
commit 2f9f0f604a
11 changed files with 707 additions and 8 deletions

View file

@ -15,12 +15,20 @@ import { FavoritesScreen } from '@/screens/FavoritesScreen';
import { SessionsScreen } from '@/screens/SessionsScreen';
import { MeterDetailScreen } from '@/screens/MeterDetailScreen';
import { AboutScreen } from '@/screens/AboutScreen';
import { AccountScreen } from '@/screens/AccountScreen';
import { ProfileScreen } from '@/screens/ProfileScreen';
import { VehiclesScreen } from '@/screens/VehiclesScreen';
import { PaymentMethodsScreen } from '@/screens/PaymentMethodsScreen';
import { useTheme } from '@/theme/ThemeContext';
import type { Zone } from 'parksmarter-client';
export type RootStackParamList = {
Tabs: undefined;
MeterDetail: { zone: Zone };
About: undefined;
Profile: undefined;
Vehicles: undefined;
PaymentMethods: undefined;
};
export type TabParamList = {
@ -28,6 +36,7 @@ export type TabParamList = {
Scan: undefined;
Favorites: undefined;
Sessions: undefined;
Account: undefined;
};
const Stack = createNativeStackNavigator<RootStackParamList>();
@ -47,6 +56,7 @@ const TAB_ICONS: Record<keyof TabParamList, keyof typeof Ionicons.glyphMap> = {
Scan: 'qr-code',
Favorites: 'star',
Sessions: 'time',
Account: 'person',
};
function Tabs() {
@ -65,23 +75,32 @@ function Tabs() {
<Tab.Screen name="Scan" component={ScanScreen} />
<Tab.Screen name="Favorites" component={FavoritesScreen} />
<Tab.Screen name="Sessions" component={SessionsScreen} />
<Tab.Screen name="Account" component={AccountScreen} />
</Tab.Navigator>
);
}
export function RootNavigator() {
const { status } = useAuth();
const { navTheme } = useTheme();
if (status === 'loading') {
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<ActivityIndicator size="large" />
<View
style={{
flex: 1,
alignItems: 'center',
justifyContent: 'center',
backgroundColor: navTheme.colors.background,
}}
>
<ActivityIndicator size="large" color={navTheme.colors.primary} />
</View>
);
}
return (
<NavigationContainer>
<NavigationContainer theme={navTheme}>
{status === 'signedIn' ? (
<Stack.Navigator>
<Stack.Screen name="Tabs" component={Tabs} options={{ headerShown: false }} />
@ -91,6 +110,13 @@ export function RootNavigator() {
options={{ title: 'Meter' }}
/>
<Stack.Screen name="About" component={AboutScreen} options={{ title: 'About' }} />
<Stack.Screen name="Profile" component={ProfileScreen} options={{ title: 'Profile' }} />
<Stack.Screen name="Vehicles" component={VehiclesScreen} options={{ title: 'Vehicles' }} />
<Stack.Screen
name="PaymentMethods"
component={PaymentMethodsScreen}
options={{ title: 'Payment methods' }}
/>
</Stack.Navigator>
) : (
<LoginScreen />