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.

This commit is contained in:
mohamad 2025-05-09 00:10:37 +02:00
parent db5f2d089e
commit 5a910a29e2
3 changed files with 49 additions and 30 deletions

View File

@ -172,11 +172,11 @@ import type { OfflineAction } from 'src/stores/offline';
interface ConflictData {
localVersion: {
data: Record<string, any>;
data: Record<string, unknown>;
timestamp: number;
};
serverVersion: {
data: Record<string, any>;
data: Record<string, unknown>;
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<string, any> }): void;
(e: 'resolve', resolution: { version: 'local' | 'server' | 'merge'; action: OfflineAction; mergedData?: Record<string, unknown> }): 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<string, any> = {};
const mergedData: Record<string, unknown> = {};
Object.entries(mergeChoices.value).forEach(([key, choice]) => {
const localValue = props.conflictData?.localVersion.data[key];
const serverValue = props.conflictData?.serverVersion.data[key];

View File

@ -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';
}

View File

@ -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<string, unknown>;
timestamp: number;
};
serverVersion: {
data: Record<string, unknown>;
timestamp: number;
};
action: OfflineAction;
}
export const useOfflineStore = defineStore('offline', () => {
const $q = useQuasar();
const isOnline = ref(navigator.onLine);
const pendingActions = ref<OfflineAction[]>([]);
const isProcessingQueue = ref(false);
const showConflictDialog = ref(false);
const currentConflict = ref<ConflictResolution | null>(null);
const currentConflict = ref<ConflictData | null>(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', () => {
(async () => {
isOnline.value = true;
processQueue();
await processQueue();
})().catch(error => {
console.error('Error processing queue:', error);
});
});
window.addEventListener('offline', () => {