import { defineStore } from 'pinia' import { ref, computed } from 'vue' import { customerRepository } from '@/services/repositories/CustomerRepository' import { CUSTOMER_STATUS, CUSTOMER_TYPES, CUSTOMER_PRIORITY } from '@/types/customer' export const useCustomersStore = defineStore('customers', () => { // State const customers = ref([]) const currentCustomer = ref(null) const loading = ref(false) const error = ref(null) const searchCriteria = ref({}) const pagination = ref({ page: 0, size: 20, totalElements: 0, totalPages: 0 }) const lastFetch = ref(null) // Getters const customersById = computed(() => { return customers.value.reduce((acc, customer) => { acc[customer.id] = customer return acc }, {}) }) const activeCustomers = computed(() => { return customers.value.filter(customer => customer && customer.status === CUSTOMER_STATUS.ACTIVE) }) const highPriorityCustomers = computed(() => { return customers.value.filter(customer => customer && (customer.priority === CUSTOMER_PRIORITY.HIGH || customer.priority === CUSTOMER_PRIORITY.CRITICAL) && customer.status === CUSTOMER_STATUS.ACTIVE ) }) const customersByType = computed(() => { return Object.values(CUSTOMER_TYPES).reduce((acc, type) => { acc[type] = customers.value.filter(customer => customer && customer.type === type) return acc }, {}) }) const customersByDepartment = computed(() => { return customers.value.reduce((acc, customer) => { if (customer && customer.department) { if (!acc[customer.department]) { acc[customer.department] = [] } acc[customer.department].push(customer) } return acc }, {}) }) const customersByLocation = computed(() => { return customers.value.reduce((acc, customer) => { if (customer && customer.location) { if (!acc[customer.location]) { acc[customer.location] = [] } acc[customer.location].push(customer) } return acc }, {}) }) const customerStats = computed(() => { const total = customers.value.length const active = customers.value.filter(c => c && c.status === CUSTOMER_STATUS.ACTIVE).length const inactive = customers.value.filter(c => c && c.status === CUSTOMER_STATUS.INACTIVE).length const pending = customers.value.filter(c => c && c.status === CUSTOMER_STATUS.PENDING).length const suspended = customers.value.filter(c => c && c.status === CUSTOMER_STATUS.SUSPENDED).length // Group by type const byType = Object.values(CUSTOMER_TYPES).reduce((acc, type) => { acc[type] = customers.value.filter(c => c && c.type === type).length return acc }, {}) // Group by priority const byPriority = Object.values(CUSTOMER_PRIORITY).reduce((acc, priority) => { acc[priority] = customers.value.filter(c => c && c.priority === priority).length return acc }, {}) // SLA levels const slaLevels = customers.value.reduce((acc, customer) => { if (customer && customer.slaLevel) { acc[customer.slaLevel] = (acc[customer.slaLevel] || 0) + 1 } return acc }, {}) // Notification preferences const emailNotifications = customers.value.filter(c => c && c.emailNotifications).length const smsNotifications = customers.value.filter(c => c && c.smsNotifications).length const selfServiceEnabled = customers.value.filter(c => c && c.allowSelfService).length // Unique departments and locations const departments = [...new Set(customers.value.map(c => c && c.department).filter(Boolean))] const locations = [...new Set(customers.value.map(c => c && c.location).filter(Boolean))] return { total, active, inactive, pending, suspended, byType, byPriority, slaLevels, emailNotifications, smsNotifications, selfServiceEnabled, departments: departments.length, locations: locations.length } }) const getCustomerById = computed(() => (id) => { return customersById.value[id] || null }) const getCustomersByType = computed(() => (type) => { return customersByType.value[type] || [] }) const getCustomersByDepartment = computed(() => (department) => { return customersByDepartment.value[department] || [] }) const getCustomersByLocation = computed(() => (location) => { return customersByLocation.value[location] || [] }) const filteredCustomers = computed(() => { if (!searchCriteria.value || Object.keys(searchCriteria.value).length === 0) { return customers.value } return customers.value.filter(customer => { // Apply search filters if (searchCriteria.value.search) { const searchTerm = searchCriteria.value.search.toLowerCase() const matchesName = customer.name.toLowerCase().includes(searchTerm) const matchesCode = customer.code?.toLowerCase().includes(searchTerm) const matchesContact = customer.contactPerson?.toLowerCase().includes(searchTerm) const matchesEmail = customer.email?.toLowerCase().includes(searchTerm) const matchesDepartment = customer.department?.toLowerCase().includes(searchTerm) const matchesLocation = customer.location?.toLowerCase().includes(searchTerm) if (!matchesName && !matchesCode && !matchesContact && !matchesEmail && !matchesDepartment && !matchesLocation) { return false } } if (searchCriteria.value.type && customer.type !== searchCriteria.value.type) { return false } if (searchCriteria.value.status && customer.status !== searchCriteria.value.status) { return false } if (searchCriteria.value.priority && customer.priority !== searchCriteria.value.priority) { return false } if (searchCriteria.value.department && customer.department !== searchCriteria.value.department) { return false } if (searchCriteria.value.location && customer.location !== searchCriteria.value.location) { return false } if (searchCriteria.value.slaLevel && customer.slaLevel !== searchCriteria.value.slaLevel) { return false } return true }) }) // Actions const initializeCustomers = async (force = false) => { const cacheExpiry = 5 * 60 * 1000 // 5 minutes const now = Date.now() if (!force && lastFetch.value && (now - lastFetch.value) < cacheExpiry) { return } loading.value = true error.value = null try { const data = await customerRepository.getAll() customers.value = data lastFetch.value = now } catch (err) { error.value = err.message console.error('Failed to initialize customers:', err) throw err } finally { loading.value = false } } const fetchCustomers = async (filters = {}) => { loading.value = true error.value = null searchCriteria.value = { ...filters } try { const searchFilters = { ...filters, page: filters.page || 0, size: filters.size || 20 } const response = await customerRepository.search(searchFilters) if (response.content) { // Paginated response customers.value = response.content pagination.value = { page: response.number || 0, size: response.size || 20, totalElements: response.totalElements || 0, totalPages: response.totalPages || 0 } } else { // Simple array response customers.value = response } } catch (err) { error.value = err.message console.error('Failed to fetch customers:', err) throw err } finally { loading.value = false } } const createCustomer = async (customerData) => { loading.value = true error.value = null try { const newCustomer = await customerRepository.create(customerData) customers.value.push(newCustomer) return newCustomer } catch (err) { error.value = err.message console.error('Failed to create customer:', err) throw err } finally { loading.value = false } } const updateCustomer = async (id, customerData) => { loading.value = true error.value = null try { const updatedCustomer = await customerRepository.update(id, customerData) // Update in local state const index = customers.value.findIndex(c => c.id === id) if (index !== -1) { customers.value[index] = updatedCustomer } // Update current customer if it's the one being edited if (currentCustomer.value?.id === id) { currentCustomer.value = updatedCustomer } return updatedCustomer } catch (err) { error.value = err.message console.error('Failed to update customer:', err) throw err } finally { loading.value = false } } const deleteCustomer = async (id) => { loading.value = true error.value = null try { await customerRepository.delete(id) // Remove from local state customers.value = customers.value.filter(c => c.id !== id) // Clear current customer if it's the one being deleted if (currentCustomer.value?.id === id) { currentCustomer.value = null } } catch (err) { error.value = err.message console.error('Failed to delete customer:', err) throw err } finally { loading.value = false } } const setCurrentCustomer = async (id) => { if (!id) { currentCustomer.value = null return } // Check if customer is already in local state const customer = getCustomerById.value(id) if (customer) { currentCustomer.value = customer return customer } // Fetch from API loading.value = true error.value = null try { const customer = await customerRepository.getById(id) currentCustomer.value = customer // Add to local state if not already there if (!customers.value.find(c => c.id === id)) { customers.value.push(customer) } return customer } catch (err) { error.value = err.message console.error('Failed to fetch customer:', err) throw err } finally { loading.value = false } } const updateCustomerStatus = async (id, status) => { loading.value = true error.value = null try { const updatedCustomer = await customerRepository.updateStatus(id, status) // Update in local state const index = customers.value.findIndex(c => c.id === id) if (index !== -1) { customers.value[index] = { ...customers.value[index], status } } return updatedCustomer } catch (err) { error.value = err.message console.error('Failed to update customer status:', err) throw err } finally { loading.value = false } } const updateCustomerPriority = async (id, priority) => { loading.value = true error.value = null try { const updatedCustomer = await customerRepository.updatePriority(id, priority) // Update in local state const index = customers.value.findIndex(c => c.id === id) if (index !== -1) { customers.value[index] = { ...customers.value[index], priority } } return updatedCustomer } catch (err) { error.value = err.message console.error('Failed to update customer priority:', err) throw err } finally { loading.value = false } } const updateNotificationSettings = async (id, settings) => { loading.value = true error.value = null try { const updatedCustomer = await customerRepository.updateNotificationSettings(id, settings) // Update in local state const index = customers.value.findIndex(c => c.id === id) if (index !== -1) { customers.value[index] = { ...customers.value[index], ...settings } } return updatedCustomer } catch (err) { error.value = err.message console.error('Failed to update notification settings:', err) throw err } finally { loading.value = false } } const bulkUpdateCustomers = async (updates) => { loading.value = true error.value = null try { const updatedCustomers = await customerRepository.bulkUpdate(updates) // Update local state updatedCustomers.forEach(updatedCustomer => { const index = customers.value.findIndex(c => c.id === updatedCustomer.id) if (index !== -1) { customers.value[index] = updatedCustomer } }) return updatedCustomers } catch (err) { error.value = err.message console.error('Failed to bulk update customers:', err) throw err } finally { loading.value = false } } const validateCustomerCode = async (code, excludeId = null) => { try { const result = await customerRepository.validateCode(code, excludeId) return result.isValid } catch (err) { console.error('Failed to validate customer code:', err) return false } } const getDepartments = async () => { try { return await customerRepository.getDepartments() } catch (err) { console.error('Failed to fetch departments:', err) return [] } } const getLocations = async () => { try { return await customerRepository.getLocations() } catch (err) { console.error('Failed to fetch locations:', err) return [] } } const refreshCustomers = async () => { lastFetch.value = null await initializeCustomers(true) } const clearError = () => { error.value = null } const clearCurrentCustomer = () => { currentCustomer.value = null } const clearCustomers = () => { customers.value = [] currentCustomer.value = null lastFetch.value = null searchCriteria.value = {} pagination.value = { page: 0, size: 20, totalElements: 0, totalPages: 0 } } const loadMockData = () => { customers.value = [ { id: 1, customerCode: 'CUST-001', name: 'ABC Corporation', email: 'contact@abc-corp.com', phone: '+1 (555) 123-4567', type: CUSTOMER_TYPES.EXTERNAL, priority: CUSTOMER_PRIORITY.HIGH, status: CUSTOMER_STATUS.ACTIVE, description: 'Large manufacturing company requiring 24/7 support', serviceHours: '24x7', sla: { responseTime: '2h', resolutionTime: '8h', availability: 99.9 }, notifications: { email: true, sms: true, phone: false, portal: true }, primaryContact: { name: 'John Smith', title: 'Facilities Manager', email: 'j.smith@abc-corp.com', phone: '+1 (555) 123-4568' }, address: { street: '123 Industrial Blvd', city: 'Manufacturing City', state: 'CA', zipCode: '90210' }, lastContactDate: '2024-01-15T10:30:00Z', lastContactMethod: 'Email', createdAt: '2023-06-15T09:00:00Z', updatedAt: '2024-01-15T10:30:00Z' }, { id: 2, customerCode: 'CUST-002', name: 'City Health Department', email: 'facilities@cityhealth.gov', phone: '+1 (555) 987-6543', type: CUSTOMER_TYPES.GOVERNMENT, priority: CUSTOMER_PRIORITY.MEDIUM, status: CUSTOMER_STATUS.ACTIVE, description: 'Municipal health department with multiple facilities', serviceHours: 'business', sla: { responseTime: '4h', resolutionTime: '24h', availability: 99.5 }, notifications: { email: true, sms: false, phone: true, portal: false }, primaryContact: { name: 'Sarah Johnson', title: 'Operations Director', email: 's.johnson@cityhealth.gov', phone: '+1 (555) 987-6544' }, address: { street: '456 Government Plaza', city: 'Capital City', state: 'CA', zipCode: '90211' }, lastContactDate: '2024-01-10T14:15:00Z', lastContactMethod: 'Phone', createdAt: '2023-08-20T11:30:00Z', updatedAt: '2024-01-10T14:15:00Z' }, { id: 3, customerCode: 'CUST-003', name: 'Tech Innovations LLC', email: 'admin@techinnovations.com', phone: '+1 (555) 456-7890', type: CUSTOMER_TYPES.PARTNER, priority: CUSTOMER_PRIORITY.LOW, status: CUSTOMER_STATUS.INACTIVE, description: 'Technology partner requiring occasional maintenance support', serviceHours: 'extended', sla: { responseTime: '8h', resolutionTime: '48h', availability: 95.0 }, notifications: { email: true, sms: false, phone: false, portal: true }, primaryContact: { name: 'Mike Chen', title: 'IT Manager', email: 'm.chen@techinnovations.com', phone: '+1 (555) 456-7891' }, address: { street: '789 Tech Park Dr', city: 'Silicon Valley', state: 'CA', zipCode: '90212' }, lastContactDate: '2023-12-05T16:45:00Z', lastContactMethod: 'Portal', createdAt: '2023-04-10T13:20:00Z', updatedAt: '2023-12-05T16:45:00Z' } ] lastFetch.value = Date.now() } return { // State customers: computed(() => customers.value), currentCustomer: computed(() => currentCustomer.value), loading: computed(() => loading.value), error: computed(() => error.value), searchCriteria: computed(() => searchCriteria.value), pagination: computed(() => pagination.value), // Getters customersById, activeCustomers, highPriorityCustomers, customersByType, customersByDepartment, customersByLocation, customerStats, getCustomerById, getCustomersByType, getCustomersByDepartment, getCustomersByLocation, filteredCustomers, // Options for dropdowns customerTypeOptions: computed(() => { return Object.values(CUSTOMER_TYPES).map(type => ({ label: type.charAt(0).toUpperCase() + type.slice(1), value: type })) }), priorityOptions: computed(() => { return Object.values(CUSTOMER_PRIORITY).map(priority => ({ label: priority.charAt(0).toUpperCase() + priority.slice(1), value: priority })) }), statusOptions: computed(() => { return Object.values(CUSTOMER_STATUS).map(status => ({ label: status.charAt(0).toUpperCase() + status.slice(1), value: status })) }), responseTimeOptions: computed(() => [ { label: '1 Hour', value: '1h' }, { label: '2 Hours', value: '2h' }, { label: '4 Hours', value: '4h' }, { label: '8 Hours', value: '8h' }, { label: '24 Hours', value: '24h' }, { label: '48 Hours', value: '48h' } ]), resolutionTimeOptions: computed(() => [ { label: '4 Hours', value: '4h' }, { label: '8 Hours', value: '8h' }, { label: '24 Hours', value: '24h' }, { label: '48 Hours', value: '48h' }, { label: '72 Hours', value: '72h' }, { label: '1 Week', value: '1w' } ]), serviceHoursOptions: computed(() => [ { label: '24x7', value: '24x7' }, { label: 'Business Hours (9AM-5PM)', value: 'business' }, { label: 'Extended Hours (7AM-7PM)', value: 'extended' }, { label: 'Custom', value: 'custom' } ]), // Statistics totalCustomers: computed(() => customers.value.length), activeCustomersCount: computed(() => activeCustomers.value.length), inactiveCustomersCount: computed(() => customers.value.filter(c => c && c.status === CUSTOMER_STATUS.INACTIVE).length), highPriorityCustomersCount: computed(() => highPriorityCustomers.value.length), averageResponseTime: computed(() => { const responseTimes = customers.value .map(c => c && c.sla?.responseTime) .filter(Boolean) .map(time => parseInt(time)) if (responseTimes.length === 0) return null const avg = responseTimes.reduce((sum, time) => sum + time, 0) / responseTimes.length return `${Math.round(avg)}h` }), // Actions initializeCustomers, fetchCustomers, createCustomer, updateCustomer, deleteCustomer, setCurrentCustomer, updateCustomerStatus, updateCustomerPriority, updateNotificationSettings, bulkUpdateCustomers, validateCustomerCode, getDepartments, getLocations, refreshCustomers, clearError, clearCurrentCustomer, clearCustomers, loadMockData } })