165 lines
4.8 KiB
TypeScript
165 lines
4.8 KiB
TypeScript
import { defineStore } from 'pinia'
|
||
import { ref, reactive } from 'vue'
|
||
import { useFetchJson } from '@/composable/useFetchJson'
|
||
import type {
|
||
ProductCategory,
|
||
ProductRelated,
|
||
ProductForm,
|
||
ProductFormProduct,
|
||
ProductFormPrice,
|
||
ProductFormVariant,
|
||
ProductVariantForm,
|
||
ProductImage,
|
||
} from '@/types/product'
|
||
import type { MenuItem } from '@/types'
|
||
import { settings } from '@/router/settings'
|
||
|
||
// ── Default values ────────────────────────────────────────────────────────────
|
||
|
||
function emptyForm(): ProductForm {
|
||
return {
|
||
product: {
|
||
reference: '',
|
||
base_price: 0,
|
||
quantity: 0,
|
||
minimal_quantity: 1,
|
||
available_for_order: true,
|
||
available_date: '',
|
||
out_of_stock_behavior: 2,
|
||
on_sale: false,
|
||
show_price: true,
|
||
condition: 'new',
|
||
is_virtual: false,
|
||
weight: 0,
|
||
width: 0,
|
||
height: 0,
|
||
depth: 0,
|
||
delivery_days: null,
|
||
active: true,
|
||
visibility: 'both',
|
||
indexed: true,
|
||
date_add: '',
|
||
date_upd: '',
|
||
name: '',
|
||
description: '',
|
||
description_short: '',
|
||
manufacturer: '',
|
||
category: '',
|
||
is_favorite: false,
|
||
is_oem: false,
|
||
is_new: false,
|
||
},
|
||
price: {
|
||
base: 0,
|
||
final_tax_excl: 0,
|
||
final_tax_incl: 0,
|
||
tax_rate: 0,
|
||
priority: 0,
|
||
},
|
||
variants: [],
|
||
}
|
||
}
|
||
|
||
// ── Store ─────────────────────────────────────────────────────────────────────
|
||
|
||
export const useAddProductStore = defineStore('addProduct', () => {
|
||
const loading = ref(false)
|
||
const saving = ref(false)
|
||
const error = ref<string | null>(null)
|
||
const successMessage = ref<string | null>(null)
|
||
|
||
// Form data for creating / editing a product
|
||
const form = reactive<ProductForm>(emptyForm())
|
||
|
||
// Variants loaded when editing an existing product
|
||
const variants = ref<ProductVariantForm[]>([])
|
||
const variantSaving = ref<Record<number, boolean>>({})
|
||
const variantErrors = ref<Record<number, string>>({})
|
||
|
||
// Images
|
||
const images = ref<ProductImage[]>([])
|
||
|
||
// Categories & related products
|
||
const selectedCategories = ref<ProductCategory[]>([])
|
||
const relatedProducts = ref<ProductRelated[]>([])
|
||
|
||
// ── Product ─────────────────────────────────────────────────────────
|
||
async function loadProduct(productId: number) {
|
||
loading.value = true
|
||
error.value = null
|
||
try {
|
||
// const resp = await useFetchJson<ProductForm>(`/api/v1/restricted/admin/product/${productId}`)
|
||
// Object.assign(form, resp.items)
|
||
console.log('[addProduct] loadProduct – API not connected yet', productId)
|
||
} catch (e: unknown) {
|
||
error.value = e instanceof Error ? e.message : 'Failed to load product'
|
||
} finally {
|
||
loading.value = false
|
||
}
|
||
}
|
||
|
||
// ── Images ────────────────────────────────────────────────────────────────
|
||
function addImageFiles(files: FileList | File[]) {
|
||
const incoming = Array.from(files)
|
||
for (const file of incoming) {
|
||
const previewUrl = URL.createObjectURL(file)
|
||
images.value.push({ previewUrl, file, cover: false, uploading: false })
|
||
}
|
||
if (!images.value.some(i => i.cover) && images.value.length > 0) {
|
||
images.value[0]!.cover = true
|
||
}
|
||
}
|
||
|
||
function removeImage(index: number) {
|
||
const img = images.value[index]!
|
||
if (img.previewUrl.startsWith('blob:')) URL.revokeObjectURL(img.previewUrl)
|
||
const wasCover = img.cover
|
||
images.value.splice(index, 1)
|
||
if (wasCover && images.value.length > 0) {
|
||
images.value[0]!.cover = true
|
||
}
|
||
}
|
||
|
||
function setCover(index: number) {
|
||
images.value.forEach((img, i) => { img.cover = i === index })
|
||
}
|
||
|
||
|
||
function resetForm() {
|
||
Object.assign(form, emptyForm())
|
||
variants.value = []
|
||
images.value = []
|
||
selectedCategories.value = []
|
||
relatedProducts.value = []
|
||
error.value = null
|
||
successMessage.value = null
|
||
}
|
||
|
||
const categories = ref<MenuItem[]>([])
|
||
async function loadCategories() {
|
||
const resp = await useFetchJson<MenuItem>(`/api/v1/restricted/menu/get-category-tree?root_category_id=${settings['app'].category_tree_root_id}`);
|
||
categories.value = resp.items.children
|
||
}
|
||
|
||
return {
|
||
loading,
|
||
saving,
|
||
error,
|
||
successMessage,
|
||
form,
|
||
images,
|
||
selectedCategories,
|
||
relatedProducts,
|
||
variants,
|
||
variantSaving,
|
||
variantErrors,
|
||
categories,
|
||
loadProduct,
|
||
addImageFiles,
|
||
removeImage,
|
||
setCover,
|
||
resetForm,
|
||
loadCategories
|
||
}
|
||
})
|