From 5a910a29e20ebe656b467326c6ccdbd4166a8d0c Mon Sep 17 00:00:00 2001 From: mohamad Date: Fri, 9 May 2025 00:10:37 +0200 Subject: [PATCH] Refactor data types in ConflictResolutionDialog and OfflineIndicator components for improved type safety; update OfflineAction interface to use 'unknown' instead of 'any' for data property, and enhance action label handling in OfflineIndicator for better clarity. --- .../components/ConflictResolutionDialog.vue | 15 ++++---- fe/src/components/OfflineIndicator.vue | 26 ++++++++----- fe/src/stores/offline.ts | 38 ++++++++++++------- 3 files changed, 49 insertions(+), 30 deletions(-) diff --git a/fe/src/components/ConflictResolutionDialog.vue b/fe/src/components/ConflictResolutionDialog.vue index 7733cfa..e536175 100644 --- a/fe/src/components/ConflictResolutionDialog.vue +++ b/fe/src/components/ConflictResolutionDialog.vue @@ -172,11 +172,11 @@ import type { OfflineAction } from 'src/stores/offline'; interface ConflictData { localVersion: { - data: Record; + data: Record; timestamp: number; }; serverVersion: { - data: Record; + data: Record; timestamp: number; }; action: OfflineAction; @@ -189,7 +189,7 @@ const props = defineProps<{ const emit = defineEmits<{ (e: 'update:modelValue', value: boolean): void; - (e: 'resolve', resolution: { version: 'local' | 'server' | 'merge'; action: OfflineAction; mergedData?: Record }): void; + (e: 'resolve', resolution: { version: 'local' | 'server' | 'merge'; action: OfflineAction; mergedData?: Record }): void; }>(); const show = ref(props.modelValue); @@ -229,11 +229,12 @@ const formatKey = (key: string): string => { .replace(/^\w/, (c) => c.toUpperCase()); }; -const formatValue = (value: any): string => { +const formatValue = (value: unknown): string => { if (value === null || value === undefined) return '-'; if (typeof value === 'boolean') return value ? 'Yes' : 'No'; if (typeof value === 'object') return JSON.stringify(value); - return String(value); + if (typeof value === 'number' || typeof value === 'string') return String(value); + return '[Unsupported Type]'; }; const isDifferent = (key: string): boolean => { @@ -257,7 +258,7 @@ const resolveConflict = (version: 'local' | 'server' | 'merge'): void => { const applyMergedChanges = (): void => { if (!props.conflictData) return; - const mergedData: Record = {}; + const mergedData: Record = {}; Object.entries(mergeChoices.value).forEach(([key, choice]) => { const localValue = props.conflictData?.localVersion.data[key]; const serverValue = props.conflictData?.serverVersion.data[key]; @@ -285,4 +286,4 @@ const applyMergedChanges = (): void => { color: $positive; font-weight: 500; } - \ No newline at end of file + \ No newline at end of file diff --git a/fe/src/components/OfflineIndicator.vue b/fe/src/components/OfflineIndicator.vue index 43ad775..935d839 100644 --- a/fe/src/components/OfflineIndicator.vue +++ b/fe/src/components/OfflineIndicator.vue @@ -89,14 +89,22 @@ const { const getActionLabel = (action: OfflineAction) => { switch (action.type) { - case 'add': - return `Add new item: ${action.data.title || 'Untitled'}`; - case 'complete': - return `Complete item: ${action.data.title || 'Untitled'}`; - case 'update': - return `Update item: ${action.data.title || 'Untitled'}`; - case 'delete': - return `Delete item: ${action.data.title || 'Untitled'}`; + case 'add': { + const data = action.data as { title?: string }; + return `Add new item: ${data.title || 'Untitled'}`; + } + case 'complete': { + const data = action.data as { title?: string }; + return `Complete item: ${data.title || 'Untitled'}`; + } + case 'update': { + const data = action.data as { title?: string }; + return `Update item: ${data.title || 'Untitled'}`; + } + case 'delete': { + const data = action.data as { title?: string }; + return `Delete item: ${data.title || 'Untitled'}`; + } default: return 'Unknown action'; } @@ -120,4 +128,4 @@ const getActionLabel = (action: OfflineAction) => { background-color: #fff3e0; } } - \ No newline at end of file + \ No newline at end of file diff --git a/fe/src/stores/offline.ts b/fe/src/stores/offline.ts index 84533f5..bef2dd3 100644 --- a/fe/src/stores/offline.ts +++ b/fe/src/stores/offline.ts @@ -7,7 +7,7 @@ export interface OfflineAction { id: string; type: 'add' | 'complete' | 'update' | 'delete'; itemId?: string; - data: any; + data: unknown; timestamp: number; version?: number; } @@ -17,16 +17,28 @@ export interface ConflictResolution { action: OfflineAction; } +export interface ConflictData { + localVersion: { + data: Record; + timestamp: number; + }; + serverVersion: { + data: Record; + timestamp: number; + }; + action: OfflineAction; +} + export const useOfflineStore = defineStore('offline', () => { const $q = useQuasar(); const isOnline = ref(navigator.onLine); const pendingActions = ref([]); const isProcessingQueue = ref(false); const showConflictDialog = ref(false); - const currentConflict = ref(null); + const currentConflict = ref(null); // Initialize from IndexedDB - const init = async () => { + const init = () => { try { const stored = LocalStorage.getItem('offline-actions'); if (stored) { @@ -66,16 +78,11 @@ export const useOfflineStore = defineStore('offline', () => { for (const action of actions) { try { - // TODO: Implement actual API calls based on action type - // This will be implemented when we have the API endpoints await processAction(action); - - // Remove successful action pendingActions.value = pendingActions.value.filter(a => a.id !== action.id); saveToStorage(); - } catch (error: any) { - if (error.status === 409) { - // Handle version conflict + } catch (error) { + if (error instanceof Error && error.message.includes('409')) { $q.notify({ type: 'warning', message: 'Item was modified by someone else while you were offline. Please review.', @@ -101,7 +108,6 @@ export const useOfflineStore = defineStore('offline', () => { // Process a single action const processAction = async (action: OfflineAction) => { // TODO: Implement actual API calls - // This is a placeholder that will be replaced with actual API calls switch (action.type) { case 'add': // await api.addItem(action.data); @@ -121,8 +127,12 @@ export const useOfflineStore = defineStore('offline', () => { // Listen for online/offline status changes const setupNetworkListeners = () => { window.addEventListener('online', () => { - isOnline.value = true; - processQueue(); + (async () => { + isOnline.value = true; + await processQueue(); + })().catch(error => { + console.error('Error processing queue:', error); + }); }); window.addEventListener('offline', () => { @@ -154,4 +164,4 @@ export const useOfflineStore = defineStore('offline', () => { processQueue, handleConflictResolution, }; -}); \ No newline at end of file +}); \ No newline at end of file