231 lines
8.2 KiB
JavaScript
231 lines
8.2 KiB
JavaScript
/**
|
|
* IndexedDB Wrapper for Offline Colaboraciones
|
|
* Stores pending colaboraciones when offline using GUID-based IDs
|
|
*/
|
|
|
|
const ColaboracionesOfflineDB = {
|
|
dbName: 'ColaboracionesOfflineDB',
|
|
version: 1,
|
|
storeName: 'colaboraciones',
|
|
|
|
/**
|
|
* Initialize the database
|
|
*/
|
|
async init() {
|
|
return new Promise((resolve, reject) => {
|
|
const request = indexedDB.open(this.dbName, this.version);
|
|
|
|
request.onerror = () => reject(request.error);
|
|
request.onsuccess = () => resolve(request.result);
|
|
|
|
request.onupgradeneeded = (event) => {
|
|
const db = event.target.result;
|
|
|
|
// Create object store if it doesn't exist
|
|
if (!db.objectStoreNames.contains(this.storeName)) {
|
|
const objectStore = db.createObjectStore(this.storeName, {
|
|
keyPath: 'id' // GUID generated client-side
|
|
});
|
|
|
|
// Indexes for querying
|
|
objectStore.createIndex('syncStatus', 'syncStatus', { unique: false });
|
|
objectStore.createIndex('timestamp', 'timestamp', { unique: false });
|
|
objectStore.createIndex('updatedAt', 'updatedAt', { unique: false });
|
|
objectStore.createIndex('miembroId', 'miembroId', { unique: false });
|
|
}
|
|
};
|
|
});
|
|
},
|
|
|
|
/**
|
|
* Generate a GUID (v4 UUID)
|
|
*/
|
|
generateGuid() {
|
|
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
|
|
const r = Math.random() * 16 | 0;
|
|
const v = c === 'x' ? r : (r & 0x3 | 0x8);
|
|
return v.toString(16);
|
|
});
|
|
},
|
|
|
|
/**
|
|
* Add a new colaboracion to offline queue
|
|
*/
|
|
async addColaboracion(colaboracionData) {
|
|
const db = await this.init();
|
|
|
|
return new Promise((resolve, reject) => {
|
|
const transaction = db.transaction([this.storeName], 'readwrite');
|
|
const store = transaction.objectStore(this.storeName);
|
|
|
|
// Prepare record with GUID and sync metadata
|
|
const record = {
|
|
id: this.generateGuid(), // GUID generated client-side
|
|
miembroId: colaboracionData.miembroId,
|
|
mesInicial: colaboracionData.mesInicial,
|
|
anioInicial: colaboracionData.anioInicial,
|
|
mesFinal: colaboracionData.mesFinal,
|
|
anioFinal: colaboracionData.anioFinal,
|
|
montoTotal: colaboracionData.montoTotal,
|
|
observaciones: colaboracionData.observaciones || '',
|
|
tiposSeleccionados: colaboracionData.tiposSeleccionados || [],
|
|
tipoPrioritario: colaboracionData.tipoPrioritario || null,
|
|
registradoPor: colaboracionData.registradoPor || 'Usuario',
|
|
syncStatus: 'pending', // pending, syncing, synced, failed
|
|
timestamp: new Date().toISOString(),
|
|
updatedAt: new Date().toISOString(),
|
|
retryCount: 0
|
|
};
|
|
|
|
const request = store.add(record);
|
|
|
|
request.onsuccess = () => {
|
|
console.log('[OfflineDB] Colaboración guardada con ID:', record.id);
|
|
resolve(record);
|
|
};
|
|
request.onerror = () => {
|
|
console.error('[OfflineDB] Error al guardar:', request.error);
|
|
reject(request.error);
|
|
};
|
|
});
|
|
},
|
|
|
|
/**
|
|
* Get all pending colaboraciones
|
|
*/
|
|
async getPending() {
|
|
const db = await this.init();
|
|
|
|
return new Promise((resolve, reject) => {
|
|
const transaction = db.transaction([this.storeName], 'readonly');
|
|
const store = transaction.objectStore(this.storeName);
|
|
const index = store.index('syncStatus');
|
|
const request = index.getAll('pending');
|
|
|
|
request.onsuccess = () => resolve(request.result);
|
|
request.onerror = () => reject(request.error);
|
|
});
|
|
},
|
|
|
|
/**
|
|
* Get all colaboraciones (any status)
|
|
*/
|
|
async getAll() {
|
|
const db = await this.init();
|
|
|
|
return new Promise((resolve, reject) => {
|
|
const transaction = db.transaction([this.storeName], 'readonly');
|
|
const store = transaction.objectStore(this.storeName);
|
|
const request = store.getAll();
|
|
|
|
request.onsuccess = () => resolve(request.result);
|
|
request.onerror = () => reject(request.error);
|
|
});
|
|
},
|
|
|
|
/**
|
|
* Update sync status of a colaboracion
|
|
*/
|
|
async updateSyncStatus(id, status, retryCount = 0) {
|
|
const db = await this.init();
|
|
|
|
return new Promise((resolve, reject) => {
|
|
const transaction = db.transaction([this.storeName], 'readwrite');
|
|
const store = transaction.objectStore(this.storeName);
|
|
const getRequest = store.get(id);
|
|
|
|
getRequest.onsuccess = () => {
|
|
const record = getRequest.result;
|
|
if (record) {
|
|
record.syncStatus = status;
|
|
record.retryCount = retryCount;
|
|
record.lastSyncAttempt = new Date().toISOString();
|
|
|
|
const updateRequest = store.put(record);
|
|
updateRequest.onsuccess = () => resolve(record);
|
|
updateRequest.onerror = () => reject(updateRequest.error);
|
|
} else {
|
|
reject(new Error('Record not found'));
|
|
}
|
|
};
|
|
getRequest.onerror = () => reject(getRequest.error);
|
|
});
|
|
},
|
|
|
|
/**
|
|
* Remove a colaboracion by ID (after successful sync)
|
|
*/
|
|
async remove(id) {
|
|
const db = await this.init();
|
|
|
|
return new Promise((resolve, reject) => {
|
|
const transaction = db.transaction([this.storeName], 'readwrite');
|
|
const store = transaction.objectStore(this.storeName);
|
|
const request = store.delete(id);
|
|
|
|
request.onsuccess = () => {
|
|
console.log('[OfflineDB] Colaboración eliminada:', id);
|
|
resolve();
|
|
};
|
|
request.onerror = () => reject(request.error);
|
|
});
|
|
},
|
|
|
|
/**
|
|
* Get count of pending colaboraciones
|
|
*/
|
|
async getPendingCount() {
|
|
const db = await this.init();
|
|
|
|
return new Promise((resolve, reject) => {
|
|
const transaction = db.transaction([this.storeName], 'readonly');
|
|
const store = transaction.objectStore(this.storeName);
|
|
const index = store.index('syncStatus');
|
|
const request = index.count('pending');
|
|
|
|
request.onsuccess = () => resolve(request.result);
|
|
request.onerror = () => reject(request.error);
|
|
});
|
|
},
|
|
|
|
/**
|
|
* Clear all records (use with caution)
|
|
*/
|
|
async clearAll() {
|
|
const db = await this.init();
|
|
|
|
return new Promise((resolve, reject) => {
|
|
const transaction = db.transaction([this.storeName], 'readwrite');
|
|
const store = transaction.objectStore(this.storeName);
|
|
const request = store.clear();
|
|
|
|
request.onsuccess = () => {
|
|
console.log('[OfflineDB] All records cleared');
|
|
resolve();
|
|
};
|
|
request.onerror = () => reject(request.error);
|
|
});
|
|
},
|
|
|
|
/**
|
|
* Get a specific colaboracion by ID
|
|
*/
|
|
async getById(id) {
|
|
const db = await this.init();
|
|
|
|
return new Promise((resolve, reject) => {
|
|
const transaction = db.transaction([this.storeName], 'readonly');
|
|
const store = transaction.objectStore(this.storeName);
|
|
const request = store.get(id);
|
|
|
|
request.onsuccess = () => resolve(request.result);
|
|
request.onerror = () => reject(request.error);
|
|
});
|
|
}
|
|
};
|
|
|
|
// Initialize database when script loads
|
|
ColaboracionesOfflineDB.init().catch(error => {
|
|
console.error('[OfflineDB] Initialization failed:', error);
|
|
});
|