389 lines
10 KiB
JavaScript
389 lines
10 KiB
JavaScript
import { defineStore } from 'pinia'
|
|
import { ref, computed } from 'vue'
|
|
import { useLocations } from '@/composables/locations/useLocations'
|
|
|
|
/**
|
|
* Locations Store
|
|
* Global state management for location data following CLAUDE.md guidelines
|
|
* Orchestrates between composables and provides cross-component data sharing
|
|
*/
|
|
export const useLocationsStore = defineStore('locations', () => {
|
|
// ==================== COMPOSABLE INTEGRATION ====================
|
|
|
|
const {
|
|
locations,
|
|
selectedLocation,
|
|
loading,
|
|
error,
|
|
searchCriteria,
|
|
miniLocations,
|
|
searchLocations,
|
|
loadMiniLocations,
|
|
refreshLocations,
|
|
updateSearchTerm,
|
|
changePage,
|
|
changePageSize,
|
|
changeSorting,
|
|
getLocationById,
|
|
createLocation,
|
|
updateLocation,
|
|
deleteLocation,
|
|
getChildLocations,
|
|
selectLocation,
|
|
clearSelectedLocation,
|
|
validateLocation,
|
|
formatLocationName,
|
|
getLocationDisplayName,
|
|
clearError
|
|
} = useLocations()
|
|
|
|
// ==================== ADDITIONAL GLOBAL STATE ====================
|
|
|
|
/** @type {import('vue').Ref<Array>} */
|
|
const recentLocations = ref([])
|
|
|
|
/** @type {import('vue').Ref<Object>} */
|
|
const locationFilters = ref({
|
|
type: '',
|
|
status: 'all', // 'all', 'active', 'inactive'
|
|
searchTerm: ''
|
|
})
|
|
|
|
/** @type {import('vue').Ref<boolean>} */
|
|
const showLocationModal = ref(false)
|
|
|
|
/** @type {import('vue').Ref<string>} */
|
|
const locationModalMode = ref('create') // 'create', 'edit', 'view'
|
|
|
|
/** @type {import('vue').Ref<Object>} */
|
|
const locationHierarchy = ref({})
|
|
|
|
// ==================== COMPUTED PROPERTIES ====================
|
|
|
|
const activeLocations = computed(() =>
|
|
locations.value.content.filter(location => location.status !== 'inactive')
|
|
)
|
|
|
|
const inactiveLocations = computed(() =>
|
|
locations.value.content.filter(location => location.status === 'inactive')
|
|
)
|
|
|
|
const locationsByType = computed(() => {
|
|
const grouped = {}
|
|
locations.value.content.forEach(location => {
|
|
const typeKey = location.type || 'General'
|
|
if (!grouped[typeKey]) {
|
|
grouped[typeKey] = []
|
|
}
|
|
grouped[typeKey].push(location)
|
|
})
|
|
return grouped
|
|
})
|
|
|
|
const filteredLocations = computed(() => {
|
|
let filtered = locations.value.content
|
|
|
|
// Filter by type
|
|
if (locationFilters.value.type) {
|
|
filtered = filtered.filter(location =>
|
|
location.type === locationFilters.value.type
|
|
)
|
|
}
|
|
|
|
// Filter by status
|
|
if (locationFilters.value.status === 'active') {
|
|
filtered = filtered.filter(location => location.status !== 'inactive')
|
|
} else if (locationFilters.value.status === 'inactive') {
|
|
filtered = filtered.filter(location => location.status === 'inactive')
|
|
}
|
|
|
|
// Filter by search term
|
|
if (locationFilters.value.searchTerm) {
|
|
const searchTerm = locationFilters.value.searchTerm.toLowerCase()
|
|
filtered = filtered.filter(location =>
|
|
location.name?.toLowerCase().includes(searchTerm) ||
|
|
location.description?.toLowerCase().includes(searchTerm) ||
|
|
location.address?.toLowerCase().includes(searchTerm)
|
|
)
|
|
}
|
|
|
|
return filtered
|
|
})
|
|
|
|
const locationStats = computed(() => ({
|
|
total: locations.value.totalElements,
|
|
active: activeLocations.value.length,
|
|
inactive: inactiveLocations.value.length,
|
|
byType: Object.keys(locationsByType.value).reduce((stats, type) => {
|
|
stats[type] = locationsByType.value[type].length
|
|
return stats
|
|
}, {})
|
|
}))
|
|
|
|
// ==================== ACTIONS ====================
|
|
|
|
/**
|
|
* Initialize locations data
|
|
* @returns {Promise<void>}
|
|
*/
|
|
const initializeLocations = async () => {
|
|
try {
|
|
await Promise.all([
|
|
searchLocations(),
|
|
loadMiniLocations()
|
|
])
|
|
} catch (err) {
|
|
console.error('Failed to initialize locations:', err)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Search locations with filters
|
|
* @param {Object} criteria - Search criteria
|
|
* @returns {Promise<Object>} Search results
|
|
*/
|
|
const searchWithFilters = async (criteria = {}) => {
|
|
const searchParams = {
|
|
...criteria,
|
|
searchValue: locationFilters.value.searchTerm
|
|
}
|
|
|
|
return await searchLocations(searchParams)
|
|
}
|
|
|
|
/**
|
|
* Update location filters and trigger search
|
|
* @param {Object} filters - Filter updates
|
|
* @returns {Promise<Object>} Search results
|
|
*/
|
|
const updateFilters = async (filters) => {
|
|
locationFilters.value = { ...locationFilters.value, ...filters }
|
|
return await searchWithFilters()
|
|
}
|
|
|
|
/**
|
|
* Create new location and refresh data
|
|
* @param {Object} locationData - Location data
|
|
* @returns {Promise<Object>} Created location
|
|
*/
|
|
const createLocationAndRefresh = async (locationData) => {
|
|
const response = await createLocation(locationData)
|
|
// Refresh locations list after successful creation
|
|
await refreshLocations()
|
|
return response
|
|
}
|
|
|
|
/**
|
|
* Update location and refresh data
|
|
* @param {string|number} locationId - Location ID
|
|
* @param {Object} locationData - Location data
|
|
* @returns {Promise<Object>} Updated location
|
|
*/
|
|
const updateLocationAndRefresh = async (locationId, locationData) => {
|
|
const updatedLocation = await updateLocation(locationId, locationData)
|
|
await addToRecentLocations(updatedLocation)
|
|
return updatedLocation
|
|
}
|
|
|
|
/**
|
|
* Delete location and refresh data
|
|
* @param {string|number} locationId - Location ID
|
|
* @returns {Promise<Object>} Delete response
|
|
*/
|
|
const deleteLocationAndRefresh = async (locationId) => {
|
|
const response = await deleteLocation(locationId)
|
|
await refreshLocations()
|
|
return response
|
|
}
|
|
|
|
/**
|
|
* Select location and open in modal
|
|
* @param {Object} location - Location to select
|
|
* @param {string} mode - Modal mode ('view', 'edit')
|
|
*/
|
|
const openLocationModal = (location = null, mode = 'create') => {
|
|
if (location) {
|
|
selectLocation(location)
|
|
locationModalMode.value = mode
|
|
} else {
|
|
clearSelectedLocation()
|
|
locationModalMode.value = 'create'
|
|
}
|
|
showLocationModal.value = true
|
|
}
|
|
|
|
/**
|
|
* Close location modal
|
|
*/
|
|
const closeLocationModal = () => {
|
|
showLocationModal.value = false
|
|
clearSelectedLocation()
|
|
}
|
|
|
|
/**
|
|
* Add location to recent locations list
|
|
* @param {Object} location - Location to add
|
|
*/
|
|
const addToRecentLocations = (location) => {
|
|
if (!location) return
|
|
|
|
// Remove if already exists
|
|
const existingIndex = recentLocations.value.findIndex(l => l.id === location.id)
|
|
if (existingIndex !== -1) {
|
|
recentLocations.value.splice(existingIndex, 1)
|
|
}
|
|
|
|
// Add to beginning
|
|
recentLocations.value.unshift(location)
|
|
|
|
// Keep only last 10
|
|
if (recentLocations.value.length > 10) {
|
|
recentLocations.value = recentLocations.value.slice(0, 10)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get location by ID with caching
|
|
* @param {string|number} locationId - Location ID
|
|
* @returns {Promise<Object>} Location details
|
|
*/
|
|
const getLocation = async (locationId) => {
|
|
// Check if location is already in current list
|
|
const existingLocation = locations.value.content.find(l => l.id === locationId)
|
|
if (existingLocation) {
|
|
selectLocation(existingLocation)
|
|
return existingLocation
|
|
}
|
|
|
|
// Fetch from API
|
|
const location = await getLocationById(locationId)
|
|
await addToRecentLocations(location)
|
|
return location
|
|
}
|
|
|
|
/**
|
|
* Build location hierarchy for a parent location
|
|
* @param {string|number} parentLocationId - Parent location ID
|
|
* @returns {Promise<Array>} Child locations hierarchy
|
|
*/
|
|
const buildLocationHierarchy = async (parentLocationId) => {
|
|
const children = await getChildLocations(parentLocationId)
|
|
locationHierarchy.value[parentLocationId] = children
|
|
|
|
// Recursively build hierarchy for child locations
|
|
for (const child of children) {
|
|
if (child.hasChildren) {
|
|
await buildLocationHierarchy(child.id)
|
|
}
|
|
}
|
|
|
|
return children
|
|
}
|
|
|
|
/**
|
|
* Clear all filters
|
|
*/
|
|
const clearFilters = () => {
|
|
locationFilters.value = {
|
|
type: '',
|
|
status: 'all',
|
|
searchTerm: ''
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Reset store state
|
|
*/
|
|
const resetStore = () => {
|
|
clearFilters()
|
|
clearSelectedLocation()
|
|
clearError()
|
|
showLocationModal.value = false
|
|
recentLocations.value = []
|
|
locationHierarchy.value = {}
|
|
}
|
|
|
|
// ==================== LEGACY SUPPORT ====================
|
|
|
|
/**
|
|
* Legacy loadLocations method for backward compatibility
|
|
* @deprecated Use initializeLocations instead
|
|
*/
|
|
const loadLocations = async () => {
|
|
return await initializeLocations()
|
|
}
|
|
|
|
/**
|
|
* Legacy locationCount for backward compatibility
|
|
* @deprecated Use locationStats.total instead
|
|
*/
|
|
const locationCount = computed(() => locations.value.totalElements || 0)
|
|
|
|
// ==================== RETURN STORE INTERFACE ====================
|
|
|
|
return {
|
|
// State from composable
|
|
locations,
|
|
selectedLocation,
|
|
loading,
|
|
error,
|
|
searchCriteria,
|
|
miniLocations,
|
|
|
|
// Additional global state
|
|
recentLocations: computed(() => recentLocations.value),
|
|
locationFilters: computed(() => locationFilters.value),
|
|
showLocationModal: computed(() => showLocationModal.value),
|
|
locationModalMode: computed(() => locationModalMode.value),
|
|
locationHierarchy: computed(() => locationHierarchy.value),
|
|
|
|
// Computed properties
|
|
activeLocations,
|
|
inactiveLocations,
|
|
locationsByType,
|
|
filteredLocations,
|
|
locationStats,
|
|
|
|
// Actions
|
|
initializeLocations,
|
|
searchWithFilters,
|
|
updateFilters,
|
|
createLocationAndRefresh,
|
|
updateLocationAndRefresh,
|
|
deleteLocationAndRefresh,
|
|
openLocationModal,
|
|
closeLocationModal,
|
|
addToRecentLocations,
|
|
getLocation,
|
|
buildLocationHierarchy,
|
|
clearFilters,
|
|
resetStore,
|
|
|
|
// Direct composable actions
|
|
searchLocations,
|
|
loadMiniLocations,
|
|
refreshLocations,
|
|
updateSearchTerm,
|
|
changePage,
|
|
changePageSize,
|
|
changeSorting,
|
|
getLocationById,
|
|
createLocation,
|
|
updateLocation,
|
|
deleteLocation,
|
|
getChildLocations,
|
|
selectLocation,
|
|
clearSelectedLocation,
|
|
validateLocation,
|
|
formatLocationName,
|
|
getLocationDisplayName,
|
|
clearError,
|
|
|
|
// Legacy support
|
|
loadLocations,
|
|
locationCount
|
|
}
|
|
})
|
|
|
|
// Also export the legacy singular name for backward compatibility
|
|
export const useLocationStore = useLocationsStore |