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:
parent
db5f2d089e
commit
5a910a29e2
@ -172,11 +172,11 @@ import type { OfflineAction } from 'src/stores/offline';
|
|||||||
|
|
||||||
interface ConflictData {
|
interface ConflictData {
|
||||||
localVersion: {
|
localVersion: {
|
||||||
data: Record<string, any>;
|
data: Record<string, unknown>;
|
||||||
timestamp: number;
|
timestamp: number;
|
||||||
};
|
};
|
||||||
serverVersion: {
|
serverVersion: {
|
||||||
data: Record<string, any>;
|
data: Record<string, unknown>;
|
||||||
timestamp: number;
|
timestamp: number;
|
||||||
};
|
};
|
||||||
action: OfflineAction;
|
action: OfflineAction;
|
||||||
@ -189,7 +189,7 @@ const props = defineProps<{
|
|||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
(e: 'update:modelValue', value: boolean): void;
|
(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);
|
const show = ref(props.modelValue);
|
||||||
@ -229,11 +229,12 @@ const formatKey = (key: string): string => {
|
|||||||
.replace(/^\w/, (c) => c.toUpperCase());
|
.replace(/^\w/, (c) => c.toUpperCase());
|
||||||
};
|
};
|
||||||
|
|
||||||
const formatValue = (value: any): string => {
|
const formatValue = (value: unknown): string => {
|
||||||
if (value === null || value === undefined) return '-';
|
if (value === null || value === undefined) return '-';
|
||||||
if (typeof value === 'boolean') return value ? 'Yes' : 'No';
|
if (typeof value === 'boolean') return value ? 'Yes' : 'No';
|
||||||
if (typeof value === 'object') return JSON.stringify(value);
|
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 => {
|
const isDifferent = (key: string): boolean => {
|
||||||
@ -257,7 +258,7 @@ const resolveConflict = (version: 'local' | 'server' | 'merge'): void => {
|
|||||||
const applyMergedChanges = (): void => {
|
const applyMergedChanges = (): void => {
|
||||||
if (!props.conflictData) return;
|
if (!props.conflictData) return;
|
||||||
|
|
||||||
const mergedData: Record<string, any> = {};
|
const mergedData: Record<string, unknown> = {};
|
||||||
Object.entries(mergeChoices.value).forEach(([key, choice]) => {
|
Object.entries(mergeChoices.value).forEach(([key, choice]) => {
|
||||||
const localValue = props.conflictData?.localVersion.data[key];
|
const localValue = props.conflictData?.localVersion.data[key];
|
||||||
const serverValue = props.conflictData?.serverVersion.data[key];
|
const serverValue = props.conflictData?.serverVersion.data[key];
|
||||||
@ -285,4 +286,4 @@ const applyMergedChanges = (): void => {
|
|||||||
color: $positive;
|
color: $positive;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
@ -89,14 +89,22 @@ const {
|
|||||||
|
|
||||||
const getActionLabel = (action: OfflineAction) => {
|
const getActionLabel = (action: OfflineAction) => {
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
case 'add':
|
case 'add': {
|
||||||
return `Add new item: ${action.data.title || 'Untitled'}`;
|
const data = action.data as { title?: string };
|
||||||
case 'complete':
|
return `Add new item: ${data.title || 'Untitled'}`;
|
||||||
return `Complete item: ${action.data.title || 'Untitled'}`;
|
}
|
||||||
case 'update':
|
case 'complete': {
|
||||||
return `Update item: ${action.data.title || 'Untitled'}`;
|
const data = action.data as { title?: string };
|
||||||
case 'delete':
|
return `Complete item: ${data.title || 'Untitled'}`;
|
||||||
return `Delete item: ${action.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:
|
default:
|
||||||
return 'Unknown action';
|
return 'Unknown action';
|
||||||
}
|
}
|
||||||
@ -120,4 +128,4 @@ const getActionLabel = (action: OfflineAction) => {
|
|||||||
background-color: #fff3e0;
|
background-color: #fff3e0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
@ -7,7 +7,7 @@ export interface OfflineAction {
|
|||||||
id: string;
|
id: string;
|
||||||
type: 'add' | 'complete' | 'update' | 'delete';
|
type: 'add' | 'complete' | 'update' | 'delete';
|
||||||
itemId?: string;
|
itemId?: string;
|
||||||
data: any;
|
data: unknown;
|
||||||
timestamp: number;
|
timestamp: number;
|
||||||
version?: number;
|
version?: number;
|
||||||
}
|
}
|
||||||
@ -17,16 +17,28 @@ export interface ConflictResolution {
|
|||||||
action: OfflineAction;
|
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', () => {
|
export const useOfflineStore = defineStore('offline', () => {
|
||||||
const $q = useQuasar();
|
const $q = useQuasar();
|
||||||
const isOnline = ref(navigator.onLine);
|
const isOnline = ref(navigator.onLine);
|
||||||
const pendingActions = ref<OfflineAction[]>([]);
|
const pendingActions = ref<OfflineAction[]>([]);
|
||||||
const isProcessingQueue = ref(false);
|
const isProcessingQueue = ref(false);
|
||||||
const showConflictDialog = ref(false);
|
const showConflictDialog = ref(false);
|
||||||
const currentConflict = ref<ConflictResolution | null>(null);
|
const currentConflict = ref<ConflictData | null>(null);
|
||||||
|
|
||||||
// Initialize from IndexedDB
|
// Initialize from IndexedDB
|
||||||
const init = async () => {
|
const init = () => {
|
||||||
try {
|
try {
|
||||||
const stored = LocalStorage.getItem('offline-actions');
|
const stored = LocalStorage.getItem('offline-actions');
|
||||||
if (stored) {
|
if (stored) {
|
||||||
@ -66,16 +78,11 @@ export const useOfflineStore = defineStore('offline', () => {
|
|||||||
|
|
||||||
for (const action of actions) {
|
for (const action of actions) {
|
||||||
try {
|
try {
|
||||||
// TODO: Implement actual API calls based on action type
|
|
||||||
// This will be implemented when we have the API endpoints
|
|
||||||
await processAction(action);
|
await processAction(action);
|
||||||
|
|
||||||
// Remove successful action
|
|
||||||
pendingActions.value = pendingActions.value.filter(a => a.id !== action.id);
|
pendingActions.value = pendingActions.value.filter(a => a.id !== action.id);
|
||||||
saveToStorage();
|
saveToStorage();
|
||||||
} catch (error: any) {
|
} catch (error) {
|
||||||
if (error.status === 409) {
|
if (error instanceof Error && error.message.includes('409')) {
|
||||||
// Handle version conflict
|
|
||||||
$q.notify({
|
$q.notify({
|
||||||
type: 'warning',
|
type: 'warning',
|
||||||
message: 'Item was modified by someone else while you were offline. Please review.',
|
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
|
// Process a single action
|
||||||
const processAction = async (action: OfflineAction) => {
|
const processAction = async (action: OfflineAction) => {
|
||||||
// TODO: Implement actual API calls
|
// TODO: Implement actual API calls
|
||||||
// This is a placeholder that will be replaced with actual API calls
|
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
case 'add':
|
case 'add':
|
||||||
// await api.addItem(action.data);
|
// await api.addItem(action.data);
|
||||||
@ -121,8 +127,12 @@ export const useOfflineStore = defineStore('offline', () => {
|
|||||||
// Listen for online/offline status changes
|
// Listen for online/offline status changes
|
||||||
const setupNetworkListeners = () => {
|
const setupNetworkListeners = () => {
|
||||||
window.addEventListener('online', () => {
|
window.addEventListener('online', () => {
|
||||||
isOnline.value = true;
|
(async () => {
|
||||||
processQueue();
|
isOnline.value = true;
|
||||||
|
await processQueue();
|
||||||
|
})().catch(error => {
|
||||||
|
console.error('Error processing queue:', error);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
window.addEventListener('offline', () => {
|
window.addEventListener('offline', () => {
|
||||||
@ -154,4 +164,4 @@ export const useOfflineStore = defineStore('offline', () => {
|
|||||||
processQueue,
|
processQueue,
|
||||||
handleConflictResolution,
|
handleConflictResolution,
|
||||||
};
|
};
|
||||||
});
|
});
|
Loading…
Reference in New Issue
Block a user