diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx
index 4e534b5..f66ea77 100644
--- a/frontend/src/App.tsx
+++ b/frontend/src/App.tsx
@@ -82,6 +82,9 @@ import { CreateVehicleRequest } from './features/vehicles/types/vehicles.types';
import { MobileSettingsScreen } from './features/settings/mobile/MobileSettingsScreen';
import { SecurityMobileScreen } from './features/settings/mobile/SecurityMobileScreen';
import { useNavigationStore, useUserStore } from './core/store';
+import { useNeedsVehicleSelection, useDowngrade } from './features/subscription/hooks/useSubscription';
+import { useVehicles } from './features/vehicles/hooks/useVehicles';
+import { VehicleSelectionDialog } from './features/subscription/components/VehicleSelectionDialog';
import { useDataSync } from './core/hooks/useDataSync';
import { MobileDebugPanel } from './core/debug/MobileDebugPanel';
import { MobileErrorBoundary } from './core/error-boundaries/MobileErrorBoundary';
@@ -319,6 +322,25 @@ function App() {
// Initialize login notifications
useLoginNotifications();
+ // Vehicle selection check for auto-downgraded users
+ const needsVehicleSelectionQuery = useNeedsVehicleSelection();
+ const vehiclesQuery = useVehicles();
+ const downgrade = useDowngrade();
+ const queryClient = useQueryClient();
+
+ // Handle vehicle selection confirmation
+ const handleVehicleSelectionConfirm = useCallback((selectedVehicleIds: string[]) => {
+ downgrade.mutate(
+ { targetTier: 'free', vehicleIdsToKeep: selectedVehicleIds },
+ {
+ onSuccess: () => {
+ // Invalidate the needs-vehicle-selection query to clear the dialog
+ queryClient.invalidateQueries({ queryKey: ['needs-vehicle-selection'] });
+ },
+ }
+ );
+ }, [downgrade, queryClient]);
+
// Enhanced navigation and user state management
const {
activeScreen,
@@ -620,6 +642,28 @@ function App() {
);
}
+ // Check if user needs to make vehicle selection after auto-downgrade
+ // This blocks the app until user selects which vehicles to keep
+ const needsVehicleSelection = needsVehicleSelectionQuery.data?.needsSelection;
+ const vehicleMaxAllowed = needsVehicleSelectionQuery.data?.maxAllowed ?? 2;
+ const vehicleList = vehiclesQuery.data ?? [];
+
+ if (needsVehicleSelection && vehicleList.length > 0) {
+ return (
+
+ {}} // No-op - dialog is blocking
+ onConfirm={handleVehicleSelectionConfirm}
+ vehicles={vehicleList}
+ maxSelections={vehicleMaxAllowed}
+ targetTier="free"
+ />
+
+ );
+ }
+
// Mobile app rendering
if (mobileMode) {
return (
diff --git a/frontend/src/features/subscription/api/subscription.api.ts b/frontend/src/features/subscription/api/subscription.api.ts
index 06ea085..d5a9458 100644
--- a/frontend/src/features/subscription/api/subscription.api.ts
+++ b/frontend/src/features/subscription/api/subscription.api.ts
@@ -3,7 +3,10 @@ import type { CheckoutRequest, PaymentMethodUpdateRequest, DowngradeRequest, Nee
export const subscriptionApi = {
getSubscription: () => apiClient.get('/subscriptions'),
- needsVehicleSelection: () => apiClient.get('/subscriptions/needs-vehicle-selection'),
+ needsVehicleSelection: async (): Promise => {
+ const response = await apiClient.get('/subscriptions/needs-vehicle-selection');
+ return response.data;
+ },
checkout: (data: CheckoutRequest) => apiClient.post('/subscriptions/checkout', data),
cancel: () => apiClient.post('/subscriptions/cancel'),
reactivate: () => apiClient.post('/subscriptions/reactivate'),