dodalem pinia store.
dodalem dokumentacje opisujaca dzialanie store dodalem services do komunikacji z directus dodalem opis dzialania services dodalem mocki danych przygotowania do refaktoryzacji
This commit is contained in:
73
frontend/src/stores/dictionariesStore.js
Normal file
73
frontend/src/stores/dictionariesStore.js
Normal file
@@ -0,0 +1,73 @@
|
||||
import { computed, ref } from 'vue'
|
||||
import { acceptHMRUpdate, defineStore } from 'pinia'
|
||||
import { fetchDictionaries as fetchDictionariesFromApi } from 'src/services/dictionariesApi'
|
||||
|
||||
export const useDictionariesStore = defineStore('dictionaries', () => {
|
||||
const models = ref([])
|
||||
const clients = ref([])
|
||||
const finishes = ref([])
|
||||
const productionLists = ref([])
|
||||
const operations = ref([])
|
||||
const colors = ref([])
|
||||
const loadedAt = ref(null)
|
||||
const isLoading = ref(false)
|
||||
const error = ref(null)
|
||||
|
||||
const isLoaded = computed(() => loadedAt.value !== null)
|
||||
|
||||
async function fetchDictionaries({ force = false } = {}) {
|
||||
if (isLoading.value || (isLoaded.value && !force)) {
|
||||
return
|
||||
}
|
||||
|
||||
isLoading.value = true
|
||||
error.value = null
|
||||
|
||||
try {
|
||||
const data = await fetchDictionariesFromApi()
|
||||
|
||||
models.value = data.models
|
||||
clients.value = data.clients
|
||||
finishes.value = data.finishes
|
||||
productionLists.value = data.productionLists
|
||||
operations.value = data.operations
|
||||
colors.value = data.colors
|
||||
loadedAt.value = Date.now()
|
||||
} catch (err) {
|
||||
error.value = err
|
||||
throw err
|
||||
} finally {
|
||||
isLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function clear() {
|
||||
models.value = []
|
||||
clients.value = []
|
||||
finishes.value = []
|
||||
productionLists.value = []
|
||||
operations.value = []
|
||||
colors.value = []
|
||||
loadedAt.value = null
|
||||
error.value = null
|
||||
}
|
||||
|
||||
return {
|
||||
models,
|
||||
clients,
|
||||
finishes,
|
||||
productionLists,
|
||||
operations,
|
||||
colors,
|
||||
loadedAt,
|
||||
isLoading,
|
||||
error,
|
||||
isLoaded,
|
||||
fetchDictionaries,
|
||||
clear,
|
||||
}
|
||||
})
|
||||
|
||||
if (import.meta.hot) {
|
||||
import.meta.hot.accept(acceptHMRUpdate(useDictionariesStore, import.meta.hot))
|
||||
}
|
||||
141
frontend/src/stores/productSpecificationStore.js
Normal file
141
frontend/src/stores/productSpecificationStore.js
Normal file
@@ -0,0 +1,141 @@
|
||||
import { computed, ref } from 'vue'
|
||||
import { acceptHMRUpdate, defineStore } from 'pinia'
|
||||
import {
|
||||
fetchProductSpecification,
|
||||
refreshProductSpecification,
|
||||
} from 'src/services/productSpecificationApi'
|
||||
|
||||
function createEmptySpecification(productId) {
|
||||
return {
|
||||
productId,
|
||||
orderId: null,
|
||||
sourceUrl: null,
|
||||
lastFetchedAt: null,
|
||||
sections: [],
|
||||
diff: [],
|
||||
loadedAt: null,
|
||||
isLoading: false,
|
||||
isRefreshing: false,
|
||||
error: null,
|
||||
}
|
||||
}
|
||||
|
||||
export const useProductSpecificationStore = defineStore('productSpecification', () => {
|
||||
const byProductId = ref({})
|
||||
|
||||
const loadedProductIds = computed(() => Object.keys(byProductId.value).map(Number))
|
||||
|
||||
function ensureSpecification(productId) {
|
||||
if (!byProductId.value[productId]) {
|
||||
byProductId.value = {
|
||||
...byProductId.value,
|
||||
[productId]: createEmptySpecification(productId),
|
||||
}
|
||||
}
|
||||
|
||||
return byProductId.value[productId]
|
||||
}
|
||||
|
||||
function setSpecification(productId, patch) {
|
||||
const current = ensureSpecification(productId)
|
||||
|
||||
byProductId.value = {
|
||||
...byProductId.value,
|
||||
[productId]: {
|
||||
...current,
|
||||
...patch,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
function getSpecification(productId) {
|
||||
return byProductId.value[productId] ?? null
|
||||
}
|
||||
|
||||
async function fetchSpecification(productId, { force = false } = {}) {
|
||||
const current = ensureSpecification(productId)
|
||||
|
||||
if (current.isLoading || (current.loadedAt && !force)) {
|
||||
return current
|
||||
}
|
||||
|
||||
setSpecification(productId, {
|
||||
isLoading: true,
|
||||
error: null,
|
||||
})
|
||||
|
||||
try {
|
||||
const specification = await fetchProductSpecification(productId)
|
||||
|
||||
setSpecification(productId, {
|
||||
productId,
|
||||
orderId: specification.orderId,
|
||||
sourceUrl: specification.sourceUrl,
|
||||
lastFetchedAt: specification.lastFetchedAt,
|
||||
sections: specification.sections,
|
||||
diff: specification.diff,
|
||||
loadedAt: Date.now(),
|
||||
isLoading: false,
|
||||
})
|
||||
|
||||
return byProductId.value[productId]
|
||||
} catch (err) {
|
||||
setSpecification(productId, {
|
||||
isLoading: false,
|
||||
error: err,
|
||||
})
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
async function refreshSpecification(productId) {
|
||||
ensureSpecification(productId)
|
||||
setSpecification(productId, {
|
||||
isRefreshing: true,
|
||||
error: null,
|
||||
})
|
||||
|
||||
try {
|
||||
const specification = await refreshProductSpecification(productId)
|
||||
|
||||
setSpecification(productId, {
|
||||
productId,
|
||||
orderId: specification.orderId,
|
||||
sourceUrl: specification.sourceUrl,
|
||||
lastFetchedAt: specification.lastFetchedAt,
|
||||
sections: specification.sections,
|
||||
diff: specification.diff,
|
||||
loadedAt: Date.now(),
|
||||
isRefreshing: false,
|
||||
})
|
||||
|
||||
return byProductId.value[productId]
|
||||
} catch (err) {
|
||||
setSpecification(productId, {
|
||||
isRefreshing: false,
|
||||
error: err,
|
||||
})
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
function clearProduct(productId) {
|
||||
const next = { ...byProductId.value }
|
||||
delete next[productId]
|
||||
byProductId.value = next
|
||||
}
|
||||
|
||||
return {
|
||||
byProductId,
|
||||
loadedProductIds,
|
||||
ensureSpecification,
|
||||
getSpecification,
|
||||
fetchSpecification,
|
||||
refreshSpecification,
|
||||
clearProduct,
|
||||
}
|
||||
})
|
||||
|
||||
if (import.meta.hot) {
|
||||
import.meta.hot.accept(acceptHMRUpdate(useProductSpecificationStore, import.meta.hot))
|
||||
}
|
||||
126
frontend/src/stores/productTimelineStore.js
Normal file
126
frontend/src/stores/productTimelineStore.js
Normal file
@@ -0,0 +1,126 @@
|
||||
import { computed, ref } from 'vue'
|
||||
import { acceptHMRUpdate, defineStore } from 'pinia'
|
||||
import {
|
||||
createProductTimelineEvent,
|
||||
fetchProductTimeline,
|
||||
} from 'src/services/productTimelineApi'
|
||||
import { useProductsStore } from 'src/stores/productsStore'
|
||||
|
||||
function createEmptyTimeline(productId) {
|
||||
return {
|
||||
productId,
|
||||
events: [],
|
||||
loadedAt: null,
|
||||
isLoading: false,
|
||||
error: null,
|
||||
}
|
||||
}
|
||||
|
||||
export const useProductTimelineStore = defineStore('productTimeline', () => {
|
||||
const byProductId = ref({})
|
||||
|
||||
const loadedProductIds = computed(() => Object.keys(byProductId.value).map(Number))
|
||||
|
||||
function ensureTimeline(productId) {
|
||||
if (!byProductId.value[productId]) {
|
||||
byProductId.value = {
|
||||
...byProductId.value,
|
||||
[productId]: createEmptyTimeline(productId),
|
||||
}
|
||||
}
|
||||
|
||||
return byProductId.value[productId]
|
||||
}
|
||||
|
||||
function setTimeline(productId, patch) {
|
||||
const current = ensureTimeline(productId)
|
||||
|
||||
byProductId.value = {
|
||||
...byProductId.value,
|
||||
[productId]: {
|
||||
...current,
|
||||
...patch,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
function getEvents(productId) {
|
||||
return byProductId.value[productId]?.events ?? []
|
||||
}
|
||||
|
||||
async function fetchTimeline(productId, { force = false } = {}) {
|
||||
const current = ensureTimeline(productId)
|
||||
|
||||
if (current.isLoading || (current.loadedAt && !force)) {
|
||||
return current
|
||||
}
|
||||
|
||||
setTimeline(productId, {
|
||||
isLoading: true,
|
||||
error: null,
|
||||
})
|
||||
|
||||
try {
|
||||
const timeline = await fetchProductTimeline(productId)
|
||||
|
||||
setTimeline(productId, {
|
||||
events: timeline.events ?? [],
|
||||
timelinePreview: timeline.timelinePreview,
|
||||
loadedAt: Date.now(),
|
||||
isLoading: false,
|
||||
})
|
||||
|
||||
if (timeline.timelinePreview) {
|
||||
useProductsStore().applyTimelinePreviewUpdate(productId, timeline.timelinePreview)
|
||||
}
|
||||
|
||||
return byProductId.value[productId]
|
||||
} catch (err) {
|
||||
setTimeline(productId, {
|
||||
isLoading: false,
|
||||
error: err,
|
||||
})
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
async function addEvent(productId, payload) {
|
||||
const { event: createdEvent, timelinePreview } = await createProductTimelineEvent(
|
||||
productId,
|
||||
payload,
|
||||
)
|
||||
|
||||
const current = ensureTimeline(productId)
|
||||
|
||||
setTimeline(productId, {
|
||||
events: [...current.events, createdEvent],
|
||||
loadedAt: Date.now(),
|
||||
})
|
||||
|
||||
if (timelinePreview) {
|
||||
useProductsStore().applyTimelinePreviewUpdate(productId, timelinePreview)
|
||||
}
|
||||
|
||||
return createdEvent
|
||||
}
|
||||
|
||||
function clearProduct(productId) {
|
||||
const next = { ...byProductId.value }
|
||||
delete next[productId]
|
||||
byProductId.value = next
|
||||
}
|
||||
|
||||
return {
|
||||
byProductId,
|
||||
loadedProductIds,
|
||||
ensureTimeline,
|
||||
getEvents,
|
||||
fetchTimeline,
|
||||
addEvent,
|
||||
clearProduct,
|
||||
}
|
||||
})
|
||||
|
||||
if (import.meta.hot) {
|
||||
import.meta.hot.accept(acceptHMRUpdate(useProductTimelineStore, import.meta.hot))
|
||||
}
|
||||
156
frontend/src/stores/productsStore.js
Normal file
156
frontend/src/stores/productsStore.js
Normal file
@@ -0,0 +1,156 @@
|
||||
import { computed, ref } from 'vue'
|
||||
import { acceptHMRUpdate, defineStore } from 'pinia'
|
||||
import { fetchProducts } from 'src/services/productsApi'
|
||||
|
||||
const DEFAULT_LIMIT = 30
|
||||
|
||||
function createDefaultFilters() {
|
||||
return {
|
||||
search: '',
|
||||
modelId: null,
|
||||
clientId: null,
|
||||
finish: null,
|
||||
productionListId: null,
|
||||
year: null,
|
||||
}
|
||||
}
|
||||
|
||||
export const useProductsStore = defineStore('products', () => {
|
||||
const ids = ref([])
|
||||
const byId = ref({})
|
||||
const filters = ref(createDefaultFilters())
|
||||
const limit = ref(DEFAULT_LIMIT)
|
||||
const offset = ref(0)
|
||||
const total = ref(null)
|
||||
const hasMore = ref(true)
|
||||
const isLoading = ref(false)
|
||||
const error = ref(null)
|
||||
|
||||
const items = computed(() => ids.value.map((id) => byId.value[id]).filter(Boolean))
|
||||
const count = computed(() => ids.value.length)
|
||||
|
||||
function buildListParams() {
|
||||
return {
|
||||
limit: limit.value,
|
||||
offset: offset.value,
|
||||
search: filters.value.search || undefined,
|
||||
model: filters.value.modelId || undefined,
|
||||
client: filters.value.clientId || undefined,
|
||||
finish: filters.value.finish || undefined,
|
||||
productionList: filters.value.productionListId || undefined,
|
||||
year: filters.value.year || undefined,
|
||||
}
|
||||
}
|
||||
|
||||
function setProducts(products, { append = false } = {}) {
|
||||
const nextIds = append ? [...ids.value] : []
|
||||
const nextById = append ? { ...byId.value } : {}
|
||||
|
||||
for (const product of products) {
|
||||
nextById[product.id] = product
|
||||
|
||||
if (!nextIds.includes(product.id)) {
|
||||
nextIds.push(product.id)
|
||||
}
|
||||
}
|
||||
|
||||
ids.value = nextIds
|
||||
byId.value = nextById
|
||||
}
|
||||
|
||||
async function fetchFirstPage() {
|
||||
offset.value = 0
|
||||
hasMore.value = true
|
||||
return fetchNextPage({ append: false })
|
||||
}
|
||||
|
||||
async function fetchNextPage({ append = true } = {}) {
|
||||
if (isLoading.value || !hasMore.value) {
|
||||
return
|
||||
}
|
||||
|
||||
isLoading.value = true
|
||||
error.value = null
|
||||
|
||||
try {
|
||||
const { items: products, pageInfo } = await fetchProducts(buildListParams())
|
||||
|
||||
setProducts(products, { append })
|
||||
|
||||
offset.value = append ? offset.value + products.length : products.length
|
||||
total.value = pageInfo.total ?? total.value
|
||||
hasMore.value = pageInfo.hasMore ?? products.length === limit.value
|
||||
} catch (err) {
|
||||
error.value = err
|
||||
throw err
|
||||
} finally {
|
||||
isLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function setFilters(nextFilters) {
|
||||
filters.value = {
|
||||
...filters.value,
|
||||
...nextFilters,
|
||||
}
|
||||
}
|
||||
|
||||
async function applyFilters(nextFilters) {
|
||||
setFilters(nextFilters)
|
||||
await fetchFirstPage()
|
||||
}
|
||||
|
||||
function updateProduct(productId, patch) {
|
||||
const current = byId.value[productId]
|
||||
|
||||
if (!current) {
|
||||
return
|
||||
}
|
||||
|
||||
byId.value = {
|
||||
...byId.value,
|
||||
[productId]: {
|
||||
...current,
|
||||
...patch,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
function applyTimelinePreviewUpdate(productId, timelinePreview) {
|
||||
updateProduct(productId, { timelinePreview })
|
||||
}
|
||||
|
||||
function clear() {
|
||||
ids.value = []
|
||||
byId.value = {}
|
||||
offset.value = 0
|
||||
total.value = null
|
||||
hasMore.value = true
|
||||
error.value = null
|
||||
}
|
||||
|
||||
return {
|
||||
ids,
|
||||
byId,
|
||||
filters,
|
||||
limit,
|
||||
offset,
|
||||
total,
|
||||
hasMore,
|
||||
isLoading,
|
||||
error,
|
||||
items,
|
||||
count,
|
||||
fetchFirstPage,
|
||||
fetchNextPage,
|
||||
setFilters,
|
||||
applyFilters,
|
||||
updateProduct,
|
||||
applyTimelinePreviewUpdate,
|
||||
clear,
|
||||
}
|
||||
})
|
||||
|
||||
if (import.meta.hot) {
|
||||
import.meta.hot.accept(acceptHMRUpdate(useProductsStore, import.meta.hot))
|
||||
}
|
||||
54
frontend/src/stores/uiStore.js
Normal file
54
frontend/src/stores/uiStore.js
Normal file
@@ -0,0 +1,54 @@
|
||||
import { computed, ref } from 'vue'
|
||||
import { acceptHMRUpdate, defineStore } from 'pinia'
|
||||
|
||||
export const UI_PANELS = {
|
||||
ADVANCED_SEARCH: 'advancedSearch',
|
||||
PRODUCT_SPECIFICATION: 'productSpecification',
|
||||
PRODUCTION_STATUSES: 'productionStatuses',
|
||||
PRODUCT_TIMELINE: 'productTimeline',
|
||||
}
|
||||
|
||||
export const useUiStore = defineStore('ui', () => {
|
||||
const isDrawerOpen = ref(false)
|
||||
const activePanel = ref(null)
|
||||
const activeProductId = ref(null)
|
||||
const drawerPayload = ref({})
|
||||
const drawerInstanceKey = ref(0)
|
||||
|
||||
const hasActivePanel = computed(() => activePanel.value !== null)
|
||||
|
||||
function openDrawer(panel, payload = {}) {
|
||||
isDrawerOpen.value = true
|
||||
activePanel.value = panel
|
||||
activeProductId.value = payload.productId ?? null
|
||||
drawerPayload.value = payload
|
||||
drawerInstanceKey.value += 1
|
||||
}
|
||||
|
||||
function replaceDrawer(panel, payload = {}) {
|
||||
openDrawer(panel, payload)
|
||||
}
|
||||
|
||||
function closeDrawer() {
|
||||
isDrawerOpen.value = false
|
||||
activePanel.value = null
|
||||
activeProductId.value = null
|
||||
drawerPayload.value = {}
|
||||
}
|
||||
|
||||
return {
|
||||
isDrawerOpen,
|
||||
activePanel,
|
||||
activeProductId,
|
||||
drawerPayload,
|
||||
drawerInstanceKey,
|
||||
hasActivePanel,
|
||||
openDrawer,
|
||||
replaceDrawer,
|
||||
closeDrawer,
|
||||
}
|
||||
})
|
||||
|
||||
if (import.meta.hot) {
|
||||
import.meta.hot.accept(acceptHMRUpdate(useUiStore, import.meta.hot))
|
||||
}
|
||||
Reference in New Issue
Block a user