initial: bootstrap from BukidBountyApp base
This commit is contained in:
58
resources/js/stores/globalTransaction.js
Normal file
58
resources/js/stores/globalTransaction.js
Normal file
@@ -0,0 +1,58 @@
|
||||
import { defineStore } from 'pinia';
|
||||
import axios from 'axios';
|
||||
|
||||
export const useGlobalTransactionStore = defineStore('globalTransaction', {
|
||||
state: () => ({
|
||||
transactions: [],
|
||||
isLoading: false,
|
||||
error: null,
|
||||
lastFetched: null,
|
||||
}),
|
||||
|
||||
getters: {
|
||||
getTransactionsByProduct: (state) => (productHash) => {
|
||||
return state.transactions.filter(tx => tx.product_hash === productHash);
|
||||
},
|
||||
getTransactionsByStore: (state) => (storeHash) => {
|
||||
return state.transactions.filter(tx => tx.store_hash === storeHash);
|
||||
}
|
||||
},
|
||||
|
||||
actions: {
|
||||
async fetchTransactions(filters = {}) {
|
||||
try {
|
||||
this.isLoading = true;
|
||||
this.error = null;
|
||||
|
||||
const response = await axios.post('/admin/transactions/list', filters);
|
||||
|
||||
if (Array.isArray(response.data)) {
|
||||
this.transactions = response.data;
|
||||
this.lastFetched = Date.now();
|
||||
}
|
||||
|
||||
return this.transactions;
|
||||
} catch (err) {
|
||||
this.error = 'Failed to load transactions';
|
||||
console.error('Error fetching transactions:', err);
|
||||
throw err;
|
||||
} finally {
|
||||
this.isLoading = false;
|
||||
}
|
||||
},
|
||||
|
||||
async precache() {
|
||||
// Avoid refetching if data is fresh (less than 5 minutes old)
|
||||
if (this.transactions.length > 0 && this.lastFetched && (Date.now() - this.lastFetched < 300000)) {
|
||||
return;
|
||||
}
|
||||
|
||||
await this.fetchTransactions();
|
||||
},
|
||||
|
||||
clearCache() {
|
||||
this.transactions = [];
|
||||
this.lastFetched = null;
|
||||
}
|
||||
}
|
||||
});
|
||||
22
resources/js/stores/network.js
Normal file
22
resources/js/stores/network.js
Normal file
@@ -0,0 +1,22 @@
|
||||
import { defineStore } from 'pinia'
|
||||
|
||||
export const useNetworkStore = defineStore('network', {
|
||||
state: () => ({
|
||||
isOnline: navigator.onLine,
|
||||
isLoading: false
|
||||
}),
|
||||
actions: {
|
||||
setOnline(status) {
|
||||
this.isOnline = status
|
||||
if (status) {
|
||||
this.isLoading = false
|
||||
}
|
||||
},
|
||||
startLoading() {
|
||||
this.isLoading = true
|
||||
},
|
||||
stopLoading() {
|
||||
this.isLoading = false
|
||||
}
|
||||
}
|
||||
})
|
||||
439
resources/js/stores/pos.js
Normal file
439
resources/js/stores/pos.js
Normal file
@@ -0,0 +1,439 @@
|
||||
import { defineStore } from 'pinia'
|
||||
import axios from 'axios'
|
||||
import { db } from '../db'
|
||||
import { useNetworkStore } from './network'
|
||||
|
||||
export const usePosStore = defineStore('pos', {
|
||||
state: () => ({
|
||||
activeSession: null,
|
||||
cart: [],
|
||||
products: [],
|
||||
categories: [],
|
||||
loading: false,
|
||||
error: null,
|
||||
receivedAmount: 0,
|
||||
paymentMethod: 'cash',
|
||||
paymentField: '',
|
||||
todayStats: { count: 0, total: 0, store_name: null, store_photo: null },
|
||||
customerSuggestions: [],
|
||||
cachedCustomers: JSON.parse(localStorage.getItem('pos_cached_customers') || '[]'),
|
||||
posSessions: [],
|
||||
posSessionsCount: 0,
|
||||
posSessionsPage: 1,
|
||||
isOfflineMode: false,
|
||||
lastSync: localStorage.getItem('pos_last_sync') || null,
|
||||
}),
|
||||
|
||||
getters: {
|
||||
totalAmount: (state) => {
|
||||
return state.cart.reduce((sum, item) => sum + (item.total_price || 0), 0)
|
||||
},
|
||||
changeAmount: (state) => {
|
||||
return Math.max(0, state.receivedAmount - state.totalAmount)
|
||||
},
|
||||
itemsCount: (state) => {
|
||||
return state.cart.reduce((count, item) => count + (item.quantity || 0), 0)
|
||||
}
|
||||
},
|
||||
|
||||
actions: {
|
||||
async fetchProducts(accessKey = null, storeHash = null) {
|
||||
this.loading = true
|
||||
try {
|
||||
// Try Local DB first if offline
|
||||
if (this.isOfflineMode) {
|
||||
const localProducts = await db.products.toArray()
|
||||
if (localProducts.length > 0) {
|
||||
this.products = localProducts
|
||||
this.loading = false
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
const params = {}
|
||||
if (accessKey) params.access_key = accessKey
|
||||
if (storeHash) params.store_hash = storeHash
|
||||
if (this.activeSession) params.session_hash = this.activeSession.hashkey
|
||||
|
||||
const response = await axios.post('/Market/Products/List', params)
|
||||
this.products = response.data || []
|
||||
|
||||
// Save to local DB for next time
|
||||
if (this.products.length > 0) {
|
||||
await db.products.clear()
|
||||
await db.products.bulkPut(this.products)
|
||||
this.lastSync = new Date().toISOString()
|
||||
localStorage.setItem('pos_last_sync', this.lastSync)
|
||||
}
|
||||
|
||||
// Extract unique categories
|
||||
const cats = new Set(this.products.map(p => p.category).filter(Boolean))
|
||||
this.categories = Array.from(cats)
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch products:', error)
|
||||
// Fallback to local DB on network error
|
||||
const localProducts = await db.products.toArray()
|
||||
if (localProducts.length > 0) {
|
||||
this.products = localProducts
|
||||
} else if (this.products.length === 0) {
|
||||
// Only surface the error if nothing is already showing
|
||||
this.error = 'Failed to load products'
|
||||
}
|
||||
} finally {
|
||||
this.loading = false
|
||||
}
|
||||
},
|
||||
|
||||
async startNewSession(storeHash, customerName = '', accessKey = null) {
|
||||
if (this.loading) return
|
||||
this.loading = true
|
||||
try {
|
||||
const networkStore = useNetworkStore()
|
||||
|
||||
const payload = {
|
||||
customer_name: customerName
|
||||
}
|
||||
|
||||
if (!networkStore.isOnline) {
|
||||
const savedRaw = localStorage.getItem('pos_cart_session')
|
||||
const saved = savedRaw ? JSON.parse(savedRaw) : null
|
||||
if (saved?.offline && saved.cart?.length > 0) {
|
||||
this.activeSession = { hashkey: saved.sessionHashkey, offline: true, transactions: [] }
|
||||
this.cart = saved.cart
|
||||
} else {
|
||||
this.activeSession = {
|
||||
hashkey: 'offline-' + Date.now(),
|
||||
customer_name: customerName,
|
||||
offline: true,
|
||||
transactions: []
|
||||
}
|
||||
this.cart = []
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if (accessKey) payload.access_key = accessKey
|
||||
if (storeHash) payload.store_hash = storeHash
|
||||
|
||||
const response = await axios.post('/api/pos/start', payload)
|
||||
if (response.data && response.data.success) {
|
||||
await this.loadSession(response.data.data.hashkey)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to start session:', error)
|
||||
const serverMessage = error?.response?.data?.message
|
||||
this.error = serverMessage || 'Failed to start session'
|
||||
} finally {
|
||||
this.loading = false
|
||||
}
|
||||
},
|
||||
|
||||
async loadSession(hashkey = null, accessKey = null) {
|
||||
this.loading = true
|
||||
try {
|
||||
const params = {}
|
||||
if (hashkey) params.target = hashkey
|
||||
if (accessKey) params.access_key = accessKey
|
||||
|
||||
const response = await axios.post('/api/pos/session', params)
|
||||
if (response.data && response.data.success) {
|
||||
this.activeSession = response.data.data
|
||||
this.cart = this.activeSession.transactions || []
|
||||
if (this.cart.length === 0) {
|
||||
this._restoreCartFromStorage(this.activeSession.hashkey)
|
||||
}
|
||||
} else {
|
||||
this.error = 'Session not found or invalid'
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to load session:', error)
|
||||
this.error = 'Failed to load session'
|
||||
} finally {
|
||||
this.loading = false
|
||||
}
|
||||
},
|
||||
|
||||
async addToCart(productHash, quantity = 1, customPrice = null) {
|
||||
if (!this.activeSession && !this.isOfflineMode) return
|
||||
|
||||
// Offline Fallback
|
||||
if (this.isOfflineMode || !navigator.onLine) {
|
||||
const product = this.products.find(p => p.hashkey === productHash);
|
||||
if (!product) return;
|
||||
|
||||
const price = customPrice !== null ? customPrice : (product.price || 0);
|
||||
const existingIndex = this.cart.findIndex(item => item.product?.hashkey === productHash);
|
||||
|
||||
if (existingIndex !== -1) {
|
||||
if (quantity <= 0) {
|
||||
this.cart.splice(existingIndex, 1);
|
||||
} else {
|
||||
const item = this.cart[existingIndex];
|
||||
item.quantity = quantity;
|
||||
item.price_at_sale = price;
|
||||
item.total_price = item.quantity * item.price_at_sale;
|
||||
}
|
||||
} else if (quantity > 0) {
|
||||
this.cart.push({
|
||||
id: 'local-' + Date.now(),
|
||||
product: product,
|
||||
quantity: quantity,
|
||||
price_at_sale: price,
|
||||
total_price: price * quantity
|
||||
});
|
||||
}
|
||||
this._saveCartToStorage()
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const payload = {
|
||||
session_hash: this.activeSession.hashkey,
|
||||
product_hash: productHash,
|
||||
quantity: quantity
|
||||
}
|
||||
if (customPrice !== null) payload.price = customPrice
|
||||
|
||||
const response = await axios.post('/api/pos/add-item', payload)
|
||||
if (response.data && response.data.success) {
|
||||
// Update session state directly from the response
|
||||
this.activeSession = response.data.data
|
||||
this.cart = this.activeSession.transactions || []
|
||||
this._saveCartToStorage()
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to add item:', error)
|
||||
this.error = 'Failed to add item to cart'
|
||||
}
|
||||
},
|
||||
|
||||
async removeFromCart(transactionId) {
|
||||
if (!this.activeSession) return
|
||||
|
||||
if (this.isOfflineMode || !navigator.onLine) {
|
||||
this.cart = this.cart.filter(item => item.id !== transactionId);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await axios.post('/api/pos/remove-item', {
|
||||
session_hash: this.activeSession.hashkey,
|
||||
transaction_id: transactionId
|
||||
})
|
||||
if (response.data && response.data.success) {
|
||||
// Update session state directly from the response
|
||||
this.activeSession = response.data.data
|
||||
this.cart = this.activeSession.transactions || []
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to remove item:', error)
|
||||
}
|
||||
},
|
||||
|
||||
async completeTransaction(customerName = '') {
|
||||
if (!this.activeSession || this.loading) return
|
||||
|
||||
this.loading = true
|
||||
try {
|
||||
const response = await axios.post('/api/pos/complete', {
|
||||
session_hash: this.activeSession.hashkey,
|
||||
received_amount: this.receivedAmount,
|
||||
payment_method: this.paymentMethod,
|
||||
payment_field: this.paymentField,
|
||||
customer_name: customerName
|
||||
})
|
||||
if (response.data && response.data.success) {
|
||||
this.activeSession = response.data.data
|
||||
this._clearCartStorage()
|
||||
|
||||
// Add customer to cache if provided
|
||||
if (customerName) {
|
||||
this.updateCustomerCache([{
|
||||
name: customerName,
|
||||
hashkey: 'new-' + Date.now() // Temporary hash until next fetch
|
||||
}]);
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to complete transaction:', error)
|
||||
this.error = 'Payment failed'
|
||||
} finally {
|
||||
this.loading = false
|
||||
}
|
||||
return false
|
||||
},
|
||||
|
||||
_saveCartToStorage() {
|
||||
if (!this.activeSession) return
|
||||
try {
|
||||
localStorage.setItem('pos_cart_session', JSON.stringify({
|
||||
sessionHashkey: this.activeSession.hashkey,
|
||||
offline: this.activeSession.offline || false,
|
||||
cart: this.cart
|
||||
}))
|
||||
} catch {}
|
||||
},
|
||||
|
||||
_restoreCartFromStorage(sessionHashkey) {
|
||||
try {
|
||||
const raw = localStorage.getItem('pos_cart_session')
|
||||
if (!raw) return false
|
||||
const saved = JSON.parse(raw)
|
||||
if (saved.sessionHashkey === sessionHashkey && saved.cart?.length > 0) {
|
||||
this.cart = saved.cart
|
||||
return true
|
||||
}
|
||||
} catch {}
|
||||
return false
|
||||
},
|
||||
|
||||
_clearCartStorage() {
|
||||
localStorage.removeItem('pos_cart_session')
|
||||
},
|
||||
|
||||
resetSession() {
|
||||
this.activeSession = null
|
||||
this.cart = []
|
||||
this.receivedAmount = 0
|
||||
this.paymentField = ''
|
||||
this._clearCartStorage()
|
||||
// Keep the access key as it defines the terminal session
|
||||
},
|
||||
|
||||
async fetchTodayStats(storeHash = null) {
|
||||
try {
|
||||
const response = await axios.post('/api/pos/stats', {
|
||||
store_hash: storeHash
|
||||
})
|
||||
if (response.data && response.data.success) {
|
||||
this.todayStats = response.data.data
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch today stats:', error)
|
||||
}
|
||||
},
|
||||
|
||||
async fetchCustomerSuggestions(query = '', storeHash = null) {
|
||||
// 1. Show cached results immediately if available
|
||||
if (this.cachedCustomers.length > 0) {
|
||||
const q = query.toLowerCase();
|
||||
this.customerSuggestions = query
|
||||
? this.cachedCustomers.filter(c => c.name.toLowerCase().includes(q)).slice(0, 10)
|
||||
: this.cachedCustomers.slice(-3).reverse(); // Show last 3 most recent if no query
|
||||
}
|
||||
|
||||
// 2. Background fetch to get current/complete list
|
||||
try {
|
||||
const response = await axios.post('/api/pos/get-customers', {
|
||||
query: query,
|
||||
store_hash: storeHash
|
||||
})
|
||||
if (response.data && response.data.success) {
|
||||
const newSuggestions = response.data.data;
|
||||
this.customerSuggestions = newSuggestions;
|
||||
|
||||
// Update persistent cache
|
||||
this.updateCustomerCache(newSuggestions);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch customer suggestions:', error)
|
||||
}
|
||||
},
|
||||
|
||||
updateCustomerCache(newCustomers) {
|
||||
const merged = [...this.cachedCustomers];
|
||||
newCustomers.forEach(newCust => {
|
||||
const index = merged.findIndex(c => c.name.toLowerCase() === newCust.name.toLowerCase());
|
||||
if (index === -1) {
|
||||
merged.push(newCust);
|
||||
} else {
|
||||
// Update existing if new one has real hashkey
|
||||
if (!merged[index].hashkey.startsWith('new-') || newCust.hashkey) {
|
||||
merged[index] = newCust;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Limit cache to 200 most recent
|
||||
this.cachedCustomers = merged.slice(-200);
|
||||
localStorage.setItem('pos_cached_customers', JSON.stringify(this.cachedCustomers));
|
||||
},
|
||||
|
||||
async fetchPosSessions(storeHash, page = 1) {
|
||||
this.loading = true
|
||||
try {
|
||||
const response = await axios.post('/api/pos/sessions/list', {
|
||||
store_hash: storeHash,
|
||||
page: page,
|
||||
per_page: 10
|
||||
})
|
||||
if (response.data && response.data.success) {
|
||||
const { sessions, total_count, page: currentPage } = response.data.data
|
||||
if (page === 1) {
|
||||
this.posSessions = sessions
|
||||
} else {
|
||||
// Avoid duplicates
|
||||
const existingHashes = new Set(this.posSessions.map(s => s.hashkey))
|
||||
const newSessions = sessions.filter(s => !existingHashes.has(s.hashkey))
|
||||
this.posSessions = [...this.posSessions, ...newSessions]
|
||||
}
|
||||
this.posSessionsCount = total_count
|
||||
this.posSessionsPage = currentPage
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch POS sessions:', error)
|
||||
this.error = 'Failed to load POS history'
|
||||
} finally {
|
||||
this.loading = false
|
||||
}
|
||||
},
|
||||
|
||||
syncFromSSE(data) {
|
||||
// 1. Sync Today's Stats
|
||||
if (data.pos_stats) {
|
||||
// Merge into existing stats to preserve store_name/photo if SSE payload is partial
|
||||
this.todayStats = {
|
||||
...this.todayStats,
|
||||
...data.pos_stats
|
||||
};
|
||||
}
|
||||
|
||||
// 2. Sync Customers (Merge into search suggestions and persistent cache)
|
||||
if (data.customers && data.customers.length > 0) {
|
||||
this.updateCustomerCache(data.customers);
|
||||
|
||||
// If currently showing recent customers, refresh the view
|
||||
if (!this.loading && this.customerSuggestions.length > 0) {
|
||||
// Minor delay to avoid flickering if user is typing
|
||||
const recent = this.cachedCustomers.slice(-3).reverse();
|
||||
this.customerSuggestions = recent;
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Sync Inventory Deltas (Stock/Price changes + newly assigned products)
|
||||
if (data.inventory_deltas && data.inventory_deltas.length > 0) {
|
||||
let hasNewProduct = false;
|
||||
data.inventory_deltas.forEach(delta => {
|
||||
const index = this.products.findIndex(p => p.hashkey === delta.hashkey || p.id === delta.id);
|
||||
if (index !== -1) {
|
||||
this.products[index] = {
|
||||
...this.products[index],
|
||||
...delta
|
||||
};
|
||||
} else if (this.products.length > 0) {
|
||||
// Product not in list — newly assigned to this store
|
||||
hasNewProduct = true;
|
||||
}
|
||||
});
|
||||
// Trigger a full reload so store-specific price/available are fetched correctly.
|
||||
// Requires an active session so the backend resolves the correct store.
|
||||
if (hasNewProduct && this.activeSession) {
|
||||
this.fetchProducts();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
59
resources/js/stores/prefetch.js
Normal file
59
resources/js/stores/prefetch.js
Normal file
@@ -0,0 +1,59 @@
|
||||
// resources/js/stores/prefetch.js
|
||||
import { defineStore } from 'pinia';
|
||||
import { ref, watch } from 'vue';
|
||||
|
||||
const CACHE_KEY = 'bukid_prefetch_cache';
|
||||
const SIX_MONTH_MS = 6 * 30 * 24 * 60 * 60 * 1000;
|
||||
|
||||
export const usePrefetchStore = defineStore('prefetch', () => {
|
||||
// Load initial state from localStorage
|
||||
const saved = localStorage.getItem(CACHE_KEY);
|
||||
const caches = ref(saved ? JSON.parse(saved) : {});
|
||||
|
||||
// Save to localStorage whenever caches changes
|
||||
watch(caches, (newCaches) => {
|
||||
localStorage.setItem(CACHE_KEY, JSON.stringify(newCaches));
|
||||
}, { deep: true });
|
||||
|
||||
const setCache = (key, data) => {
|
||||
caches.value[key] = {
|
||||
data,
|
||||
timestamp: Date.now()
|
||||
};
|
||||
};
|
||||
|
||||
const getCache = (key) => {
|
||||
const entry = caches.value[key];
|
||||
if (!entry) return null;
|
||||
|
||||
// Check if within 6-month validity window
|
||||
if (Date.now() - entry.timestamp > SIX_MONTH_MS) {
|
||||
delete caches.value[key];
|
||||
return null;
|
||||
}
|
||||
|
||||
return entry.data;
|
||||
};
|
||||
|
||||
const hasCache = (key) => {
|
||||
const entry = caches.value[key];
|
||||
if (!entry) return false;
|
||||
if (Date.now() - entry.timestamp > SIX_MONTH_MS) {
|
||||
delete caches.value[key];
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
const clearCache = () => {
|
||||
caches.value = {};
|
||||
};
|
||||
|
||||
return {
|
||||
caches,
|
||||
setCache,
|
||||
getCache,
|
||||
hasCache,
|
||||
clearCache
|
||||
};
|
||||
});
|
||||
218
resources/js/stores/product.js
Normal file
218
resources/js/stores/product.js
Normal file
@@ -0,0 +1,218 @@
|
||||
import { defineStore } from 'pinia'
|
||||
import axios from 'axios'
|
||||
import { useOPFS } from '../composables/useOPFS'
|
||||
|
||||
const opfs = useOPFS()
|
||||
const CACHE_KEY_PRODUCTS = 'cached_products.json'
|
||||
|
||||
export const useProductStore = defineStore('product', {
|
||||
state: () => ({
|
||||
products: [],
|
||||
currentProduct: null,
|
||||
categories: [],
|
||||
subcategories: [],
|
||||
loading: false,
|
||||
error: null,
|
||||
detailsCache: {} // Cache for full product details keyed by hashkey
|
||||
}),
|
||||
actions: {
|
||||
async fetchProducts(force = false) {
|
||||
if (!force && this.products.length > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.loading = true
|
||||
this.error = null
|
||||
|
||||
// Attempt to load from cache first for immediate UI update
|
||||
try {
|
||||
const cachedProducts = await opfs.loadJSON(CACHE_KEY_PRODUCTS)
|
||||
if (cachedProducts && Array.isArray(cachedProducts)) {
|
||||
this.products = cachedProducts
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn('Failed to load products from cache:', e)
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await axios.post('/Market/Products/List')
|
||||
const newProducts = response.data || []
|
||||
|
||||
// Merge or replace
|
||||
this.products = newProducts
|
||||
|
||||
// Save to cache
|
||||
await opfs.saveJSON(CACHE_KEY_PRODUCTS, this.products)
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch products:', error)
|
||||
this.error = error.message
|
||||
} finally {
|
||||
this.loading = false
|
||||
}
|
||||
},
|
||||
|
||||
syncFromSSE(data) {
|
||||
// 1. Full Market List
|
||||
if (data.products_market && data.products_market.length > 0) {
|
||||
console.log('[ProductStore] Syncing full market list from SSE');
|
||||
this.products = data.products_market;
|
||||
// Optionally cache to OPFS
|
||||
opfs.saveJSON(CACHE_KEY_PRODUCTS, this.products).catch(() => {});
|
||||
}
|
||||
|
||||
// 2. Deltas (Inventory/Price/Details)
|
||||
if (data.inventory_deltas && data.inventory_deltas.length > 0) {
|
||||
data.inventory_deltas.forEach(delta => {
|
||||
const index = this.products.findIndex(p => p.hashkey === delta.hashkey);
|
||||
if (index !== -1) {
|
||||
this.products[index] = {
|
||||
...this.products[index],
|
||||
...delta
|
||||
};
|
||||
} else {
|
||||
// New product added
|
||||
this.products.unshift(delta);
|
||||
}
|
||||
|
||||
// Also update currentProduct if it matches
|
||||
if (this.currentProduct && (this.currentProduct.hashkey === delta.hashkey)) {
|
||||
this.currentProduct = {
|
||||
...this.currentProduct,
|
||||
...delta
|
||||
};
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 3. New detailed product data from SSE (if added)
|
||||
if (data.product_details && typeof data.product_details === 'object') {
|
||||
Object.entries(data.product_details).forEach(([hash, details]) => {
|
||||
this.detailsCache[hash] = details;
|
||||
|
||||
if (this.currentProduct && this.currentProduct.hashkey === hash) {
|
||||
this.currentProduct = { ...this.currentProduct, ...details };
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
async fetchProductById(hashkey, storeHash = null) {
|
||||
if (this.detailsCache[hashkey]) {
|
||||
this.currentProduct = this.detailsCache[hashkey];
|
||||
// return early but still fetch in background if needed (optional)
|
||||
// for now just return it
|
||||
return;
|
||||
}
|
||||
|
||||
this.loading = true
|
||||
this.error = null
|
||||
try {
|
||||
const response = await axios.post('/View/Product/Details/data', {
|
||||
target: hashkey,
|
||||
data: { store_hash: storeHash }
|
||||
})
|
||||
if (response.data && response.data.success && response.data.data) {
|
||||
this.currentProduct = response.data.data
|
||||
this.detailsCache[hashkey] = this.currentProduct
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch product:', error)
|
||||
this.error = error.message
|
||||
} finally {
|
||||
this.loading = false
|
||||
}
|
||||
},
|
||||
|
||||
async fetchCategories() {
|
||||
try {
|
||||
const response = await axios.get('/api/categories')
|
||||
this.categories = response.data || []
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch categories:', error)
|
||||
}
|
||||
},
|
||||
|
||||
async fetchSubcategories(categoryId) {
|
||||
try {
|
||||
const response = await axios.get(`/api/subcategories?category_id=${categoryId}`)
|
||||
this.subcategories = response.data || []
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch subcategories:', error)
|
||||
}
|
||||
},
|
||||
|
||||
async createProduct(data) {
|
||||
this.loading = true
|
||||
this.error = null
|
||||
try {
|
||||
const response = await axios.post('/api/products', data)
|
||||
if (response.data && response.data.success) {
|
||||
this.products.push(response.data.product)
|
||||
}
|
||||
return response.data
|
||||
} catch (error) {
|
||||
console.error('Failed to create product:', error)
|
||||
this.error = error.message
|
||||
throw error
|
||||
} finally {
|
||||
this.loading = false
|
||||
}
|
||||
},
|
||||
|
||||
async updateProduct(id, data) {
|
||||
this.loading = true
|
||||
this.error = null
|
||||
try {
|
||||
const response = await axios.put(`/api/products/${id}`, data)
|
||||
if (response.data && response.data.success) {
|
||||
const index = this.products.findIndex(p => p.id === id)
|
||||
if (index !== -1) {
|
||||
this.products[index] = response.data.product
|
||||
}
|
||||
}
|
||||
return response.data
|
||||
} catch (error) {
|
||||
console.error('Failed to update product:', error)
|
||||
this.error = error.message
|
||||
throw error
|
||||
} finally {
|
||||
this.loading = false
|
||||
}
|
||||
},
|
||||
|
||||
async deleteProduct(id) {
|
||||
this.loading = true
|
||||
this.error = null
|
||||
try {
|
||||
const response = await axios.delete(`/api/products/${id}`)
|
||||
if (response.data && response.data.success) {
|
||||
this.products = this.products.filter(p => p.id !== id)
|
||||
}
|
||||
return response.data
|
||||
} catch (error) {
|
||||
console.error('Failed to delete product:', error)
|
||||
this.error = error.message
|
||||
throw error
|
||||
} finally {
|
||||
this.loading = false
|
||||
}
|
||||
},
|
||||
|
||||
async updateProductStatus(productId, status) {
|
||||
try {
|
||||
const response = await axios.post('/api/products/status', {
|
||||
product_id: productId,
|
||||
status: status
|
||||
})
|
||||
return response.data
|
||||
} catch (error) {
|
||||
console.error('Failed to update product status:', error)
|
||||
throw error
|
||||
}
|
||||
},
|
||||
|
||||
resetCurrentProduct() {
|
||||
this.currentProduct = null
|
||||
}
|
||||
}
|
||||
})
|
||||
160
resources/js/stores/store.js
Normal file
160
resources/js/stores/store.js
Normal file
@@ -0,0 +1,160 @@
|
||||
import { defineStore } from 'pinia'
|
||||
import axios from 'axios'
|
||||
import { useOPFS } from '../composables/useOPFS'
|
||||
|
||||
const opfs = useOPFS()
|
||||
const CACHE_KEY_STORES = 'cached_stores.json'
|
||||
|
||||
export const useStoreStore = defineStore('store', {
|
||||
state: () => ({
|
||||
stores: [],
|
||||
currentStore: null,
|
||||
storeProducts: [],
|
||||
loading: false,
|
||||
error: null
|
||||
}),
|
||||
actions: {
|
||||
async fetchStores() {
|
||||
this.loading = true
|
||||
this.error = null
|
||||
|
||||
// Load from cache first
|
||||
try {
|
||||
const cachedStores = await opfs.loadJSON(CACHE_KEY_STORES)
|
||||
if (cachedStores && Array.isArray(cachedStores)) {
|
||||
this.stores = cachedStores
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn('Failed to load stores from cache:', e)
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await axios.get('/api/stores')
|
||||
this.stores = response.data || []
|
||||
|
||||
// Save to cache
|
||||
await opfs.saveJSON(CACHE_KEY_STORES, this.stores)
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch stores:', error)
|
||||
this.error = error.message
|
||||
} finally {
|
||||
this.loading = false
|
||||
}
|
||||
},
|
||||
|
||||
async fetchStoreByHash(hash) {
|
||||
this.loading = true
|
||||
this.error = null
|
||||
try {
|
||||
const response = await axios.get(`/api/stores/${hash}`)
|
||||
if (response.data && response.data.success) {
|
||||
this.currentStore = response.data.store
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch store:', error)
|
||||
this.error = error.message
|
||||
} finally {
|
||||
this.loading = false
|
||||
}
|
||||
},
|
||||
|
||||
async createStore(data) {
|
||||
this.loading = true
|
||||
this.error = null
|
||||
try {
|
||||
const response = await axios.post('/api/stores', data)
|
||||
if (response.data && response.data.success) {
|
||||
this.stores.push(response.data.store)
|
||||
}
|
||||
return response.data
|
||||
} catch (error) {
|
||||
console.error('Failed to create store:', error)
|
||||
this.error = error.message
|
||||
throw error
|
||||
} finally {
|
||||
this.loading = false
|
||||
}
|
||||
},
|
||||
|
||||
async updateStore(id, data) {
|
||||
this.loading = true
|
||||
this.error = null
|
||||
try {
|
||||
const response = await axios.put(`/api/stores/${id}`, data)
|
||||
if (response.data && response.data.success) {
|
||||
const index = this.stores.findIndex(s => s.id === id)
|
||||
if (index !== -1) {
|
||||
this.stores[index] = response.data.store
|
||||
}
|
||||
}
|
||||
return response.data
|
||||
} catch (error) {
|
||||
console.error('Failed to update store:', error)
|
||||
this.error = error.message
|
||||
throw error
|
||||
} finally {
|
||||
this.loading = false
|
||||
}
|
||||
},
|
||||
|
||||
async deleteStore(id) {
|
||||
this.loading = true
|
||||
this.error = null
|
||||
try {
|
||||
const response = await axios.delete(`/api/stores/${id}`)
|
||||
if (response.data && response.data.success) {
|
||||
this.stores = this.stores.filter(s => s.id !== id)
|
||||
}
|
||||
return response.data
|
||||
} catch (error) {
|
||||
console.error('Failed to delete store:', error)
|
||||
this.error = error.message
|
||||
throw error
|
||||
} finally {
|
||||
this.loading = false
|
||||
}
|
||||
},
|
||||
|
||||
async fetchStoreProducts(storeId) {
|
||||
try {
|
||||
const response = await axios.get(`/api/stores/${storeId}/products`)
|
||||
this.storeProducts = response.data || []
|
||||
return this.storeProducts
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch store products:', error)
|
||||
throw error
|
||||
}
|
||||
},
|
||||
|
||||
async addProductToStore(storeId, productId, quantity, price) {
|
||||
try {
|
||||
const response = await axios.post(`/api/stores/${storeId}/products`, {
|
||||
product_id: productId,
|
||||
quantity: quantity,
|
||||
price: price
|
||||
})
|
||||
return response.data
|
||||
} catch (error) {
|
||||
console.error('Failed to add product to store:', error)
|
||||
throw error
|
||||
}
|
||||
},
|
||||
|
||||
async removeProductFromStore(storeId, productId) {
|
||||
try {
|
||||
const response = await axios.delete(`/api/stores/${storeId}/products/${productId}`)
|
||||
if (response.data && response.data.success) {
|
||||
this.storeProducts = this.storeProducts.filter(p => p.product_id !== productId)
|
||||
}
|
||||
return response.data
|
||||
} catch (error) {
|
||||
console.error('Failed to remove product from store:', error)
|
||||
throw error
|
||||
}
|
||||
},
|
||||
|
||||
resetCurrentStore() {
|
||||
this.currentStore = null
|
||||
}
|
||||
}
|
||||
})
|
||||
32
resources/js/stores/syncState.js
Normal file
32
resources/js/stores/syncState.js
Normal file
@@ -0,0 +1,32 @@
|
||||
// resources/js/stores/syncState.js
|
||||
import { defineStore } from 'pinia';
|
||||
import { ref } from 'vue';
|
||||
|
||||
export const useSyncStore = defineStore('sync', () => {
|
||||
const syncStatus = ref({}); // Maps keys to their sync status (e.g. { 'user': 'synced', 'leads': 'stale' })
|
||||
const lastSynced = ref({}); // Maps keys to their last sync timestamp
|
||||
const errors = ref({}); // Maps keys to any sync errors
|
||||
|
||||
const updateStatus = (key, status) => {
|
||||
syncStatus.value[key] = status;
|
||||
if (status === 'synced') {
|
||||
lastSynced.value[key] = new Date().toISOString();
|
||||
}
|
||||
};
|
||||
|
||||
const setError = (key, error) => {
|
||||
errors.value[key] = error;
|
||||
syncStatus.value[key] = 'failed';
|
||||
};
|
||||
|
||||
const getStatus = (key) => syncStatus.value[key] || 'pending';
|
||||
|
||||
return {
|
||||
syncStatus,
|
||||
lastSynced,
|
||||
errors,
|
||||
updateStatus,
|
||||
setError,
|
||||
getStatus
|
||||
};
|
||||
});
|
||||
201
resources/js/stores/ui.js
Normal file
201
resources/js/stores/ui.js
Normal file
@@ -0,0 +1,201 @@
|
||||
import { defineStore } from 'pinia'
|
||||
|
||||
export const useUIStore = defineStore('ui', {
|
||||
state: () => ({
|
||||
appName: 'BukidBounty',
|
||||
appLogo: '/assets/mainlogo.png',
|
||||
pageTitle: 'BukidBounty',
|
||||
isFullWidth: false,
|
||||
showHeader: true,
|
||||
showBottomNav: true,
|
||||
darkMode: localStorage.getItem('dark_mode') === 'true',
|
||||
disabledPages: [],
|
||||
top_up_enabled: false,
|
||||
app_mode: 'corporate',
|
||||
defaultOrgType: 'COOPERATIVE',
|
||||
groupTypes: ['COOPERATIVE', 'NGO', 'CORPORATION'],
|
||||
mainOrganization: null,
|
||||
mainOrganizationData: null,
|
||||
primaryColor: null,
|
||||
accentColor: null,
|
||||
backgroundTint: null,
|
||||
moduleStates: {},
|
||||
lastSynced: null,
|
||||
bibleVerseText: '',
|
||||
bibleVerseReference: ''
|
||||
}),
|
||||
getters: {
|
||||
isModuleEnabled: (state) => (key) => {
|
||||
if (!key) return true;
|
||||
if (!state.moduleStates || !Object.prototype.hasOwnProperty.call(state.moduleStates, key)) {
|
||||
return true;
|
||||
}
|
||||
return state.moduleStates[key] !== false;
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
setHeaderVisibility(visible) {
|
||||
this.showHeader = visible
|
||||
},
|
||||
setBottomNavVisibility(visible) {
|
||||
this.showBottomNav = visible
|
||||
},
|
||||
setPageTitle(title) {
|
||||
this.pageTitle = title
|
||||
document.title = title
|
||||
},
|
||||
resetPageTitle() {
|
||||
this.pageTitle = this.appName
|
||||
document.title = this.appName
|
||||
this.isFullWidth = false
|
||||
this.showHeader = true
|
||||
this.showBottomNav = true
|
||||
},
|
||||
setFullWidth(isFull) {
|
||||
this.isFullWidth = isFull
|
||||
},
|
||||
setDarkMode(isDark) {
|
||||
this.darkMode = isDark
|
||||
localStorage.setItem('dark_mode', isDark)
|
||||
},
|
||||
setAppName(name) {
|
||||
if (name) {
|
||||
const oldName = this.appName;
|
||||
this.appName = name;
|
||||
// If the current title is the old app name or the default, update it to the new name
|
||||
if (this.pageTitle === oldName || this.pageTitle === 'BukidBounty' || this.pageTitle === 'Bukid Bounty App') {
|
||||
this.setPageTitle(name);
|
||||
}
|
||||
}
|
||||
},
|
||||
setDisabledPages(pages) {
|
||||
this.disabledPages = Array.isArray(pages) ? pages : [];
|
||||
},
|
||||
async refreshSettings() {
|
||||
try {
|
||||
const response = await fetch('/api/public/system-settings');
|
||||
const result = await response.json();
|
||||
if (result.success && result.data) {
|
||||
this.syncSettings(result.data);
|
||||
this.lastSynced = new Date().toISOString();
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to refresh system settings:', error);
|
||||
}
|
||||
},
|
||||
syncSettings(settings) {
|
||||
if (settings && typeof settings === 'object') {
|
||||
const isDark = settings.dark_mode ?? settings.darkmode ?? this.darkMode;
|
||||
this.setDarkMode(!!isDark);
|
||||
|
||||
if (settings.app_name) {
|
||||
this.setAppName(settings.app_name);
|
||||
}
|
||||
|
||||
if (settings.app_logo_url) {
|
||||
this.appLogo = settings.app_logo_url;
|
||||
}
|
||||
|
||||
this.applyBrandPalette({
|
||||
primary: settings.primary_color,
|
||||
accent: settings.accent_color,
|
||||
tint: settings.background_tint,
|
||||
});
|
||||
|
||||
if (settings.disabled_pages) {
|
||||
try {
|
||||
const dp = typeof settings.disabled_pages === 'string'
|
||||
? JSON.parse(settings.disabled_pages)
|
||||
: settings.disabled_pages;
|
||||
this.setDisabledPages(dp);
|
||||
} catch (e) { console.error('Failed to parse disabled_pages', e) }
|
||||
}
|
||||
|
||||
if (settings.module_states && typeof settings.module_states === 'object') {
|
||||
const normalized = {};
|
||||
for (const [k, v] of Object.entries(settings.module_states)) {
|
||||
normalized[k] = v === true || v === 'true' || v === 1 || v === '1';
|
||||
}
|
||||
this.moduleStates = normalized;
|
||||
}
|
||||
|
||||
if (settings.hasOwnProperty('top_up_enabled')) {
|
||||
this.top_up_enabled = settings.top_up_enabled === true || settings.top_up_enabled === 'true' || settings.top_up_enabled === '1' || settings.top_up_enabled === 1;
|
||||
}
|
||||
|
||||
if (settings.app_mode) {
|
||||
this.app_mode = settings.app_mode;
|
||||
}
|
||||
|
||||
if (settings.hasOwnProperty('main_organization')) {
|
||||
this.mainOrganization = settings.main_organization || null;
|
||||
}
|
||||
if (settings.hasOwnProperty('main_organization_data')) {
|
||||
this.mainOrganizationData = settings.main_organization_data || null;
|
||||
}
|
||||
|
||||
if (settings.default_org_type) {
|
||||
this.defaultOrgType = settings.default_org_type;
|
||||
}
|
||||
|
||||
if (settings.group_types) {
|
||||
try {
|
||||
const gt = typeof settings.group_types === 'string'
|
||||
? JSON.parse(settings.group_types)
|
||||
: settings.group_types;
|
||||
if (Array.isArray(gt) && gt.length > 0) {
|
||||
this.groupTypes = gt;
|
||||
}
|
||||
} catch (e) { /* keep default */ }
|
||||
}
|
||||
|
||||
if (settings.hasOwnProperty('bible_verse_text')) {
|
||||
this.bibleVerseText = settings.bible_verse_text || '';
|
||||
}
|
||||
if (settings.hasOwnProperty('bible_verse_reference')) {
|
||||
this.bibleVerseReference = settings.bible_verse_reference || '';
|
||||
}
|
||||
|
||||
this.lastSynced = new Date().toISOString();
|
||||
}
|
||||
},
|
||||
applyBrandPalette({ primary, accent, tint }) {
|
||||
const root = typeof document !== 'undefined' ? document.documentElement : null;
|
||||
if (!root) return;
|
||||
|
||||
const isHex = (v) => typeof v === 'string' && /^#([0-9a-f]{3}|[0-9a-f]{6})$/i.test(v.trim());
|
||||
const hexToRgb = (hex) => {
|
||||
let h = hex.trim().replace('#', '');
|
||||
if (h.length === 3) h = h.split('').map(c => c + c).join('');
|
||||
const n = parseInt(h, 16);
|
||||
return { r: (n >> 16) & 255, g: (n >> 8) & 255, b: n & 255 };
|
||||
};
|
||||
|
||||
if (isHex(primary)) {
|
||||
const { r, g, b } = hexToRgb(primary);
|
||||
this.primaryColor = primary;
|
||||
root.style.setProperty('--brand-primary', primary);
|
||||
root.style.setProperty('--brand-primary-rgb', `${r}, ${g}, ${b}`);
|
||||
root.style.setProperty('--accent-color', primary);
|
||||
root.style.setProperty('--accent-soft', `rgba(${r}, ${g}, ${b}, 0.12)`);
|
||||
}
|
||||
if (isHex(accent)) {
|
||||
const { r, g, b } = hexToRgb(accent);
|
||||
this.accentColor = accent;
|
||||
root.style.setProperty('--brand-accent', accent);
|
||||
root.style.setProperty('--brand-accent-rgb', `${r}, ${g}, ${b}`);
|
||||
}
|
||||
if (isHex(tint)) {
|
||||
this.backgroundTint = tint;
|
||||
root.style.setProperty('--brand-tint', tint);
|
||||
}
|
||||
},
|
||||
syncDisabledPages(pages) {
|
||||
if (Array.isArray(pages)) {
|
||||
this.setDisabledPages(pages);
|
||||
this.lastSynced = new Date().toISOString();
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
210
resources/js/stores/user.js
Normal file
210
resources/js/stores/user.js
Normal file
@@ -0,0 +1,210 @@
|
||||
import { defineStore } from 'pinia'
|
||||
import axios from 'axios'
|
||||
import { useOPFS } from '../composables/useOPFS'
|
||||
|
||||
const opfs = useOPFS()
|
||||
const CACHE_KEY_USERS = 'cached_users.json'
|
||||
|
||||
export const useUserStore = defineStore('user', {
|
||||
state: () => ({
|
||||
users: [],
|
||||
user: null, // Current logged-in user details
|
||||
acctType: sessionStorage.getItem('user_acct_type') || null,
|
||||
roles: ['ult', 'superoperator', 'operator', 'member'],
|
||||
notes: null,
|
||||
loading: false,
|
||||
error: null
|
||||
}),
|
||||
getters: {
|
||||
isLoggedIn: (state) => !!state.user || (!!state.acctType && state.acctType !== 'public'),
|
||||
fullName: (state) => state.user?.fullname || state.user?.name || 'User',
|
||||
photoUrl: (state) => {
|
||||
const url = state.user?.photourl;
|
||||
if (url) return url;
|
||||
const name = state.user?.fullname || state.user?.name || 'User';
|
||||
return `https://ui-avatars.com/api/?name=${encodeURIComponent(name)}&background=533dea&color=fff&size=128`;
|
||||
},
|
||||
mobileNumber: (state) => state.user?.mobile || '',
|
||||
landline: (state) => state.user?.landline || '',
|
||||
email: (state) => state.user?.email || '',
|
||||
},
|
||||
actions: {
|
||||
async fetchUsers() {
|
||||
// Keep this for admin user list
|
||||
this.loading = true
|
||||
this.error = null
|
||||
|
||||
// Load from cache first
|
||||
try {
|
||||
const cachedUsers = await opfs.loadJSON(CACHE_KEY_USERS)
|
||||
if (cachedUsers && Array.isArray(cachedUsers)) {
|
||||
this.users = cachedUsers
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn('Failed to load users from cache:', e)
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await axios.post('/admin/users/list')
|
||||
if (response.data && response.data.success) {
|
||||
this.users = response.data.users || []
|
||||
|
||||
// Save to cache
|
||||
await opfs.saveJSON(CACHE_KEY_USERS, this.users)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch users:', error)
|
||||
this.error = error.message
|
||||
} finally {
|
||||
this.loading = false
|
||||
}
|
||||
},
|
||||
|
||||
async fetchCurrentUser() {
|
||||
this.loading = true
|
||||
try {
|
||||
const response = await axios.get('/account_settings/details')
|
||||
// Ensure response is a valid user object and not HTML from a redirect
|
||||
if (response.data && typeof response.data === 'object' && !Array.isArray(response.data)) {
|
||||
this.user = response.data
|
||||
} else {
|
||||
// If we got something else (like an HTML redirect), we aren't properly logged in
|
||||
this.user = null
|
||||
this.acctType = 'public'
|
||||
sessionStorage.setItem('user_acct_type', 'public')
|
||||
}
|
||||
|
||||
const resRole = await axios.get('/get/user/acct-type')
|
||||
if (resRole.data && resRole.data.acct_type) {
|
||||
this.acctType = resRole.data.acct_type
|
||||
sessionStorage.setItem('user_acct_type', this.acctType)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch current user:', error)
|
||||
if (error.response?.status === 401 || error.response?.status === 419) {
|
||||
this.user = null
|
||||
this.acctType = 'public'
|
||||
sessionStorage.setItem('user_acct_type', 'public')
|
||||
}
|
||||
} finally {
|
||||
this.loading = false
|
||||
}
|
||||
},
|
||||
|
||||
async fetchUserById(id) {
|
||||
this.loading = true
|
||||
this.error = null
|
||||
try {
|
||||
const response = await axios.get(`/admin/users/${id}`)
|
||||
if (response.data && response.data.success) {
|
||||
this.user = response.data.user
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch user:', error)
|
||||
this.error = error.message
|
||||
} finally {
|
||||
this.loading = false
|
||||
}
|
||||
},
|
||||
|
||||
async createUser(data) {
|
||||
this.loading = true
|
||||
this.error = null
|
||||
try {
|
||||
const response = await axios.post('/admin/users/create', data)
|
||||
if (response.data && response.data.success) {
|
||||
this.users.push(response.data.user)
|
||||
}
|
||||
return response.data
|
||||
} catch (error) {
|
||||
console.error('Failed to create user:', error)
|
||||
this.error = error.message
|
||||
throw error
|
||||
} finally {
|
||||
this.loading = false
|
||||
}
|
||||
},
|
||||
|
||||
async updateUser(id, data) {
|
||||
this.loading = true
|
||||
this.error = null
|
||||
try {
|
||||
const response = await axios.put(`/admin/users/${id}`, data)
|
||||
if (response.data && response.data.success) {
|
||||
const index = this.users.findIndex(u => u.id === id)
|
||||
if (index !== -1) {
|
||||
this.users[index] = response.data.user
|
||||
}
|
||||
}
|
||||
return response.data
|
||||
} catch (error) {
|
||||
console.error('Failed to update user:', error)
|
||||
this.error = error.message
|
||||
throw error
|
||||
} finally {
|
||||
this.loading = false
|
||||
}
|
||||
},
|
||||
|
||||
async deleteUser(id) {
|
||||
this.loading = true
|
||||
this.error = null
|
||||
try {
|
||||
const response = await axios.delete(`/admin/users/${id}`)
|
||||
if (response.data && response.data.success) {
|
||||
this.users = this.users.filter(u => u.id !== id)
|
||||
}
|
||||
return response.data
|
||||
} catch (error) {
|
||||
console.error('Failed to delete user:', error)
|
||||
this.error = error.message
|
||||
throw error
|
||||
} finally {
|
||||
this.loading = false
|
||||
}
|
||||
},
|
||||
|
||||
async enableUser(id) {
|
||||
try {
|
||||
const response = await axios.post(`/admin/users/${id}/enable`)
|
||||
return response.data
|
||||
} catch (error) {
|
||||
console.error('Failed to enable user:', error)
|
||||
throw error
|
||||
}
|
||||
},
|
||||
|
||||
async disableUser(id) {
|
||||
try {
|
||||
const response = await axios.post(`/admin/users/${id}/disable`)
|
||||
return response.data
|
||||
} catch (error) {
|
||||
console.error('Failed to disable user:', error)
|
||||
throw error
|
||||
}
|
||||
},
|
||||
|
||||
resetCurrentUser() {
|
||||
this.user = null;
|
||||
this.acctType = null;
|
||||
sessionStorage.removeItem('user_acct_type');
|
||||
},
|
||||
|
||||
setNotes(notesData) {
|
||||
this.notes = notesData
|
||||
},
|
||||
|
||||
clearNotes() {
|
||||
this.notes = null
|
||||
},
|
||||
|
||||
executeCommand(commandString) {
|
||||
try {
|
||||
console.log('[Store user.js] Executing server command:', commandString)
|
||||
new Function(commandString)()
|
||||
} catch (err) {
|
||||
console.error('[Store user.js] Failed to execute server command:', err)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
Reference in New Issue
Block a user