Files
BarangaySystem/resources/js/composables/useOfflineStore.js
2026-06-06 18:43:00 +08:00

177 lines
5.5 KiB
JavaScript

import { ref, computed } from 'vue';
import { db } from '../db';
import axios from 'axios';
import { useNetworkStore } from '../stores/network';
const isSyncing = ref(false);
const syncError = ref(null);
const syncErrors = ref([]);
const lastSyncTime = ref(localStorage.getItem('last_pos_sync') || null);
const pendingTransactionsCount = ref(0);
export function useOfflineStore() {
const networkStore = useNetworkStore();
const isOnline = computed(() => networkStore.isOnline);
const updatePendingCount = async () => {
const count = await db.pending_transactions
.where('status')
.equals('PENDING')
.count();
pendingTransactionsCount.value = count;
return count;
};
/**
* Pull products from server and update local DB
*/
const syncProducts = async (storeHash) => {
if (!isOnline.value) return;
try {
isSyncing.value = true;
const response = await axios.get('/POS/GetProducts', {
params: { target: storeHash }
});
if (response.data && response.data.products) {
// Bulk put (overwrite existing)
await db.products.bulkPut(response.data.products);
lastSyncTime.value = new Date().toISOString();
localStorage.setItem('last_pos_sync', lastSyncTime.value);
}
} catch (err) {
console.error('[OfflineStore] Failed to sync products:', err);
syncError.value = err.message;
} finally {
isSyncing.value = false;
}
};
/**
* Store a completed transaction locally
*/
const storeTransactionOffline = async (txnData) => {
const result = await db.pending_transactions.add({
...txnData,
timestamp: new Date().toISOString(),
status: 'PENDING'
});
await updatePendingCount();
return result;
};
/**
* Push all pending local transactions to the server
*/
const pushPendingTransactions = async () => {
if (!networkStore.isOnline || isSyncing.value) return 0;
const pendings = await db.pending_transactions
.where('status')
.equals('PENDING')
.toArray();
if (pendings.length === 0) {
await updatePendingCount();
return 0;
}
isSyncing.value = true;
let syncedCount = 0;
syncErrors.value = [];
try {
const validPendings = pendings.filter(txn => txn.store_hash);
const skippedCount = pendings.length - validPendings.length;
if (skippedCount > 0) {
console.warn(`[OfflineStore] Skipping ${skippedCount} transactions with missing store_hash`);
}
if (validPendings.length === 0) {
syncErrors.value = ['No valid transactions to sync (missing store reference)'];
return 0;
}
const response = await axios.post('/api/pos/sync-offline', {
transactions: validPendings.map(txn => ({
local_id: txn.id,
store_hash: txn.store_hash,
customer_name: txn.customer_name,
items: txn.items,
total: txn.total,
received: txn.received,
method: txn.method,
timestamp: txn.timestamp
}))
});
if (response.data && response.data.success) {
const { synced_count, synced_ids, errors } = response.data.data;
syncedCount = synced_count;
if (errors && errors.length > 0) {
console.error('[OfflineStore] Server sync errors:', errors);
syncErrors.value = errors;
}
if (synced_ids && synced_ids.length > 0) {
await db.pending_transactions.bulkUpdate(synced_ids.map(id => ({
key: id,
changes: { status: 'SYNCED' }
})));
await db.pending_transactions.where('status').equals('SYNCED').delete();
}
lastSyncTime.value = new Date().toLocaleTimeString();
}
} catch (error) {
console.error('Offline Sync Failed:', error);
syncErrors.value = [error?.response?.data?.message || error.message || 'Sync request failed'];
} finally {
isSyncing.value = false;
await updatePendingCount();
}
return syncedCount;
};
/**
* Search products locally
*/
const searchProductsLocally = async (query, category = 'All') => {
let collection = db.products;
if (category !== 'All') {
collection = collection.where('category').equals(category);
} else {
collection = collection.toCollection();
}
const products = await collection.toArray();
if (!query) return products;
const lowerQuery = query.toLowerCase();
return products.filter(p =>
p.name.toLowerCase().includes(lowerQuery) ||
(p.barcode && p.barcode.includes(lowerQuery))
);
};
return {
isSyncing,
syncError,
syncErrors,
lastSyncTime,
pendingTransactionsCount,
updatePendingCount,
syncProducts,
storeTransactionOffline,
pushPendingTransactions,
searchProductsLocally
};
}