1 import { useState, useEffect, useCallback } from 'react';
2 import { useQuery, useQueryClient } from '@tanstack/react-query';
3 import {
4 isOfflineSupported,
5 isOnline,
6 loadOfflineData,
7 loadAllOfflineDataAsync,
8 saveOfflineData,
9 syncAllOfflineData,
10 clearAllOfflineCache,
11 getStorageUsage,
12 type OfflineData
13 } from '@/lib/offline-storage';
14 import type { RepairGuide, TroubleshootingFlow, DeviceComponent } from '@shared/schema';
15 import { TECHNICAL_MANUALS, type TechnicalManual } from '../../shared/technical-manuals';
16
17 export function useOfflineMode() {
18 const [isOfflineModeEnabled, setIsOfflineModeEnabled] = useState(true);
19 const [isCurrentlyOnline, setIsCurrentlyOnline] = useState(isOnline());
20 const [lastSyncTime, setLastSyncTime] = useState<Date | null>(null);
21 const [isSyncing, setIsSyncing] = useState(false);
22 const [syncProgress, setSyncProgress] = useState<number>(0);
23 const [syncStatusText, setSyncStatusText] = useState<string>('');
24 const [storageUsageState, setStorageUsageState] = useState(getStorageUsage());
25 const queryClient = useQueryClient();
26
27 const refreshStorageInfo = useCallback(() => {
28 setStorageUsageState(getStorageUsage());
29 }, []);
30
31 // Monitor online/offline status
32 useEffect(() => {
33 const handleOnline = () => {
34 setIsCurrentlyOnline(true);
35 };
36
37 const handleOffline = () => {
38 setIsCurrentlyOnline(false);
39 };
40
41 window.addEventListener('online', handleOnline);
42 window.addEventListener('offline', handleOffline);
43
44 return () => {
45 window.removeEventListener('online', handleOnline);
46 window.removeEventListener('offline', handleOffline);
47 };
48 }, []);
49
50 // Load offline data on mount
51 useEffect(() => {
52 if (isOfflineSupported()) {
53 const offlineData = loadOfflineData();
54 if (offlineData.lastSync) {
55 setLastSyncTime(new Date(offlineData.lastSync));
56 }
57 refreshStorageInfo();
58 }
59 }, [refreshStorageInfo]);
60
61 // Full library sync (624 guides + 8 manuals)
62 const fullSync = useCallback(async (): Promise<{ success: boolean; guidesCount: number; manualsCount: number }> => {
63 setIsSyncing(true);
64 setSyncProgress(5);
65 setSyncStatusText('Initializing offline sync engine...');
66
67 try {
68 const result = await syncAllOfflineData((progress, status) => {
69 setSyncProgress(progress);
70 setSyncStatusText(status);
71 });
72
73 if (result.success) {
74 setLastSyncTime(new Date());
75 refreshStorageInfo();
76 queryClient.invalidateQueries({ queryKey: ['/api/repair-guides'] });
77 queryClient.invalidateQueries({ queryKey: ['/api/technical-manuals'] });
78 queryClient.invalidateQueries({ queryKey: ['/api/troubleshooting-flows'] });
79 queryClient.invalidateQueries({ queryKey: ['/api/device-components'] });
80 queryClient.invalidateQueries({ queryKey: ['offline-guides'] });
81 queryClient.invalidateQueries({ queryKey: ['offline-manuals'] });
82 }
83
84 setIsSyncing(false);
85 return result;
86 } catch (error: any) {
87 setIsSyncing(false);
88 setSyncStatusText(`Error: ${error?.message || 'Sync failed'}`);
89 return { success: false, guidesCount: 0, manualsCount: 0 };
90 }
91 }, [queryClient, refreshStorageInfo]);
92
93 // Clear offline cache
94 const clearCache = useCallback(async (): Promise<void> => {
95 await clearAllOfflineCache();
96 setLastSyncTime(null);
97 refreshStorageInfo();
98 queryClient.invalidateQueries({ queryKey: ['offline-guides'] });
99 queryClient.invalidateQueries({ queryKey: ['offline-manuals'] });
100 }, [queryClient, refreshStorageInfo]);
101
102 // Enable offline mode
103 const enableOfflineMode = async (): Promise<boolean> => {
104 setIsOfflineModeEnabled(true);
105 if (isCurrentlyOnline) {
106 const res = await fullSync();
107 return res.success;
108 }
109 return true;
110 };
111
112 // Disable offline mode
113 const disableOfflineMode = () => {
114 setIsOfflineModeEnabled(false);
115 };
116
117 // Get offline data synchronously
118 const getOfflineData = (): OfflineData => {
119 return loadOfflineData();
120 };
121
122 // Get storage usage information
123 const storageInfo = {
124 used: storageUsageState.used,
125 available: Math.max(0, storageUsageState.total - storageUsageState.used),
126 percentage: storageUsageState.percentage,
127 breakdown: storageUsageState.breakdown,
128 };
129
130 // Query for offline guides
131 const { data: offlineGuides = [] } = useQuery<RepairGuide[]>({
132 queryKey: ['offline-guides'],
133 queryFn: async () => {
134 const data = await loadAllOfflineDataAsync();
135 return data.guides;
136 },
137 staleTime: 60000,
138 });
139
140 // Query for offline manuals
141 const { data: offlineManuals = TECHNICAL_MANUALS } = useQuery<TechnicalManual[]>({
142 queryKey: ['offline-manuals'],
143 queryFn: async () => {
144 const data = await loadAllOfflineDataAsync();
145 return data.manuals && data.manuals.length > 0 ? data.manuals : TECHNICAL_MANUALS;
146 },
147 staleTime: 60000,
148 });
149
150 const { data: offlineFlows = [] } = useQuery<TroubleshootingFlow[]>({
151 queryKey: ['offline-flows'],
152 queryFn: async () => {
153 const data = await loadAllOfflineDataAsync();
154 return data.flows;
155 },
156 staleTime: 60000,
157 });
158
159 const { data: offlineComponents = [] } = useQuery<DeviceComponent[]>({
160 queryKey: ['offline-components'],
161 queryFn: async () => {
162 const data = await loadAllOfflineDataAsync();
163 return data.components;
164 },
165 staleTime: 60000,
166 });
167
168 return {
169 // Status
170 isOfflineSupported: isOfflineSupported(),
171 isOfflineModeEnabled,
172 isCurrentlyOnline,
173 lastSyncTime,
174 storageInfo,
175 isSyncing,
176 syncProgress,
177 syncStatusText,
178
179 // Actions
180 enableOfflineMode,
181 disableOfflineMode,
182 forceSync: fullSync,
183 fullSync,
184 clearCache,
185 refreshStorageInfo,
186 getOfflineData,
187
188 // Offline data
189 offlineGuides,
190 offlineManuals,
191 offlineFlows,
192 offlineComponents,
193 };
194 }