@eka-care/patient-ts-sdk
Version:
TypeScript SDK for Trinity Patient Profile Management System
247 lines (246 loc) • 9.94 kB
JavaScript
/**
* IndexedDB service for local patient data storage
*/
export class IndexedDBService {
constructor(workspaceId) {
this.dbName = 'TrinityProfilesDB';
this.version = 1;
this.db = null;
this.workspaceId = workspaceId;
}
/**
* Initialize the IndexedDB connection
*/
async init() {
return new Promise((resolve, reject) => {
const request = indexedDB.open(this.dbName, this.version);
request.onerror = () => reject(request.error);
request.onsuccess = () => {
this.db = request.result;
// Create object store for patients with workspace-specific naming
// const storeName = this.getStoreName();
// if (!this.db.objectStoreNames.contains(storeName)) {
// const store = this.db.createObjectStore(storeName, { keyPath: 'oid' });
// // Create indexes for search fields
// store.createIndex('fln', 'fln', { unique: false });
// store.createIndex('mobile', 'mobile', { unique: false });
// store.createIndex('username', 'username', { unique: false });
// store.createIndex('u_ate', 'u_ate', { unique: false });
// }
resolve();
};
request.onupgradeneeded = (event) => {
const db = event.target.result;
// Create object store for patients with workspace-specific naming
const storeName = this.getStoreName();
if (!db.objectStoreNames.contains(storeName)) {
const store = db.createObjectStore(storeName, { keyPath: 'oid' });
// Create indexes for search fields
store.createIndex('fln', 'fln', { unique: false });
store.createIndex('mobile', 'mobile', { unique: false });
store.createIndex('username', 'username', { unique: false });
store.createIndex('u_ate', 'u_ate', { unique: false });
}
};
});
}
/**
* Get workspace-specific store name
*/
getStoreName() {
return `patients_${this.workspaceId}`;
}
/**
* Store multiple patients in batch
*/
async batchStore(patients) {
if (!this.db)
throw new Error('Database not initialized');
// return new Promise((resolve, reject) => {
// const transaction = this.db!.transaction([this.getStoreName()], 'readwrite');
// const store = transaction.objectStore(this.getStoreName());
// transaction.oncomplete = () => resolve();
// transaction.onerror = () => reject(transaction.error);
// patients.forEach(patient => {
// store.put(patient);
// });
// });
const storeName = this.getStoreName();
if (!this.db.objectStoreNames.contains(storeName)) {
// Close current connection and reinitialize
this.close();
await this.init();
// Check again after reinitialization
if (!this.db?.objectStoreNames.contains(storeName)) {
throw new Error(`Object store '${storeName}' still not found after reinitialization`);
}
}
return new Promise((resolve, reject) => {
try {
const transaction = this.db.transaction([this.getStoreName()], 'readwrite');
const store = transaction.objectStore(this.getStoreName());
transaction.oncomplete = () => {
console.log('Batch store transaction completed successfully');
resolve();
};
transaction.onerror = () => {
console.error('Batch store transaction error:', transaction.error);
reject(transaction.error);
};
patients.forEach(patient => {
store.put(patient);
});
}
catch (error) {
console.error('Error creating transaction:', error);
reject(error);
}
});
}
/**
* Search patients by prefix with field-specific logic
*/
async searchByPrefix(prefix, limit = 50) {
if (!this.db)
throw new Error('Database not initialized');
const results = [];
const isNumeric = /^\d+$/.test(prefix);
const lowerPrefix = prefix.toLowerCase();
return new Promise((resolve, reject) => {
const transaction = this.db.transaction([this.getStoreName()], 'readonly');
const store = transaction.objectStore(this.getStoreName());
const request = store.openCursor();
request.onsuccess = (event) => {
const cursor = event.target.result;
if (cursor && results.length < limit) {
const patient = cursor.value;
let match = false;
if (isNumeric) {
// Search in mobile and username fields for numeric prefix
if (patient.mobile?.startsWith(prefix) ||
patient.username?.toLowerCase().startsWith(lowerPrefix)) {
match = true;
}
}
else {
// Search in fln and username fields for alphabetic prefix
if (patient.fln?.toLowerCase().startsWith(lowerPrefix) ||
patient.username?.toLowerCase().startsWith(lowerPrefix)) {
match = true;
}
}
if (match) {
results.push(patient);
}
cursor.continue();
}
else {
resolve(results);
}
};
request.onerror = () => reject(request.error);
});
}
/**
* Get patient by OID (primary key)
*/
async getByOid(oid) {
if (!this.db)
throw new Error('Database not initialized');
return new Promise((resolve, reject) => {
const transaction = this.db.transaction([this.getStoreName()], 'readonly');
const store = transaction.objectStore(this.getStoreName());
const request = store.get(oid);
request.onsuccess = () => resolve(request.result || null);
request.onerror = () => reject(request.error);
});
}
/**
* Update single patient by OID
*/
async updatePatient(patient) {
if (!this.db)
throw new Error('Database not initialized');
return new Promise((resolve, reject) => {
const transaction = this.db.transaction([this.getStoreName()], 'readwrite');
const store = transaction.objectStore(this.getStoreName());
const request = store.put(patient);
request.onsuccess = () => resolve();
request.onerror = () => reject(request.error);
});
}
/**
* Check if any data exists
*/
async hasData() {
if (!this.db)
throw new Error('Database not initialized');
return new Promise((resolve, reject) => {
const transaction = this.db.transaction([this.getStoreName()], 'readonly');
const store = transaction.objectStore(this.getStoreName());
const request = store.count();
request.onsuccess = () => resolve(request.result > 0);
request.onerror = () => reject(request.error);
});
}
/**
* Get all patients (for internal use)
*/
async getAllPatients() {
if (!this.db)
throw new Error('Database not initialized');
return new Promise((resolve, reject) => {
const transaction = this.db.transaction([this.getStoreName()], 'readonly');
const store = transaction.objectStore(this.getStoreName());
const request = store.getAll();
request.onsuccess = () => resolve(request.result);
request.onerror = () => reject(request.error);
});
}
/**
* Get the latest update timestamp
*/
async getLatestUpdateTime() {
if (!this.db)
throw new Error('Database not initialized');
return new Promise((resolve, reject) => {
const transaction = this.db.transaction([this.getStoreName()], 'readonly');
const store = transaction.objectStore(this.getStoreName());
const index = store.index('u_ate');
const request = index.openCursor(null, 'prev');
request.onsuccess = () => {
const cursor = request.result;
if (cursor) {
resolve(cursor.value.u_ate);
}
else {
resolve(0);
}
};
request.onerror = () => reject(request.error);
});
}
/**
* Clear all data for the current workspace
*/
async clearData() {
if (!this.db)
throw new Error('Database not initialized');
return new Promise((resolve, reject) => {
const transaction = this.db.transaction([this.getStoreName()], 'readwrite');
const store = transaction.objectStore(this.getStoreName());
const request = store.clear();
request.onsuccess = () => resolve();
request.onerror = () => reject(request.error);
});
}
/**
* Close the database connection
*/
close() {
if (this.db) {
this.db.close();
this.db = null;
}
}
}