119 lines
4.1 KiB
Vue
119 lines
4.1 KiB
Vue
<template>
|
|
<div class="space-y-6">
|
|
|
|
<!-- Header bar -->
|
|
<div class="flex items-center justify-between flex-wrap gap-3">
|
|
<div class="flex items-center gap-3">
|
|
<UIcon name="i-lucide-arrow-left"
|
|
class="cursor-pointer text-(--text-sky-light) dark:text-(--text-sky-dark) text-xl"
|
|
@click="$router.back()" />
|
|
<h1 class="text-2xl font-bold text-black dark:text-white">
|
|
{{ isEditMode ? 'Edit Product' : 'Add Product' }}
|
|
</h1>
|
|
</div>
|
|
|
|
<div class="flex items-center gap-3">
|
|
<div class="flex items-center gap-2">
|
|
<span class="text-sm text-gray-500 dark:text-gray-400">Active</span>
|
|
<USwitch v-model="store.form.active" color="success" />
|
|
</div>
|
|
<UButton variant="outline" color="neutral" @click="handleCancel">Cancel</UButton>
|
|
<UButton color="info" :loading="store.saving" @click="handleSave">
|
|
<UIcon name="i-lucide-save" class="text-base" />
|
|
Save
|
|
</UButton>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Alerts -->
|
|
<UAlert v-if="store.error" color="error" variant="subtle" :title="store.error" icon="i-lucide-alert-circle" />
|
|
<UAlert v-if="store.successMessage" color="success" variant="subtle" :title="store.successMessage"
|
|
icon="i-lucide-check-circle" />
|
|
|
|
<!-- Tabs -->
|
|
<UTabs :items="tabs" color="info" :ui="{ root: 'gap-6' }">
|
|
<template #general>
|
|
<ProductTabGeneral />
|
|
</template>
|
|
<template #pricing>
|
|
<ProductTabPricing />
|
|
</template>
|
|
<template #quantities>
|
|
<ProductTabQuantities />
|
|
</template>
|
|
<template #shipping>
|
|
<ProductTabShipping />
|
|
</template>
|
|
<template #seo>
|
|
<ProductTabSeo />
|
|
</template>
|
|
<template #options>
|
|
<ProductTabOptions />
|
|
</template>
|
|
<template #variants>
|
|
<ProductTabVariants :is-edit-mode="isEditMode" />
|
|
</template>
|
|
</UTabs>
|
|
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { onMounted, computed } from 'vue'
|
|
import { useRouter } from 'vue-router'
|
|
import { useAddProductStore } from '@/stores/admin/addProduct'
|
|
import ProductTabGeneral from './product/TabGeneral.vue'
|
|
import ProductTabPricing from './product/TabPricing.vue'
|
|
import ProductTabQuantities from './product/TabQuantities.vue'
|
|
import ProductTabShipping from './product/TabShipping.vue'
|
|
import ProductTabSeo from './product/TabSeo.vue'
|
|
import ProductTabOptions from './product/TabOptions.vue'
|
|
import ProductTabVariants from './product/TabVariants.vue'
|
|
|
|
const props = defineProps<{
|
|
productId?: number
|
|
}>()
|
|
|
|
const router = useRouter()
|
|
const store = useAddProductStore()
|
|
const isEditMode = computed(() => !!props.productId)
|
|
|
|
const tabs = computed(() => [
|
|
{ label: 'General', slot: 'general' },
|
|
{ label: 'Pricing', slot: 'pricing' },
|
|
{ label: 'Quantities', slot: 'quantities' },
|
|
{ label: 'Shipping', slot: 'shipping' },
|
|
{ label: 'SEO', slot: 'seo' },
|
|
{ label: 'Options', slot: 'options' },
|
|
...(isEditMode.value ? [{ label: 'Variants', slot: 'variants' }] : []),
|
|
])
|
|
|
|
onMounted(async () => {
|
|
store.resetForm()
|
|
if (isEditMode.value && props.productId) {
|
|
await store.loadProduct(props.productId)
|
|
await store.loadVariants(props.productId)
|
|
}
|
|
})
|
|
|
|
async function handleSave() {
|
|
if (!store.form.name.trim()) {
|
|
store.error = 'Product name is required.'
|
|
return
|
|
}
|
|
if (isEditMode.value && props.productId) {
|
|
await store.updateProduct(props.productId)
|
|
} else {
|
|
const newId = await store.createProduct()
|
|
if (newId) {
|
|
router.push({ name: 'admin-product-edit', params: { product_id: newId } })
|
|
}
|
|
}
|
|
}
|
|
|
|
function handleCancel() {
|
|
store.resetForm()
|
|
router.push({ name: 'admin-products' })
|
|
}
|
|
</script>
|