nostrmarket/static/components/product-list.js
Ben Weeks bd010ece6b style: fix prettier formatting
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-23 12:21:59 +00:00

261 lines
7.5 KiB
JavaScript

window.app.component('product-list', {
name: 'product-list',
template: '#product-list',
delimiters: ['${', '}'],
props: ['adminkey', 'inkey', 'stall-filter'],
data: function () {
return {
filter: '',
stalls: [],
products: [],
pendingProducts: [],
selectedStall: null,
productDialog: {
showDialog: false,
showRestore: false,
data: null
},
productsTable: {
columns: [
{name: 'name', align: 'left', label: 'Name', field: 'name'},
{name: 'stall', align: 'left', label: 'Stall', field: 'stall_id'},
{name: 'price', align: 'left', label: 'Price', field: 'price'},
{
name: 'quantity',
align: 'left',
label: 'Quantity',
field: 'quantity'
},
{name: 'actions', align: 'right', label: 'Actions', field: ''}
],
pagination: {
rowsPerPage: 10
}
}
}
},
computed: {
stallOptions: function () {
return this.stalls.map(s => ({
label: s.name,
value: s.id
}))
},
filteredProducts: function () {
if (!this.selectedStall) {
return this.products
}
return this.products.filter(p => p.stall_id === this.selectedStall)
}
},
watch: {
stallFilter: {
immediate: true,
handler(newVal) {
if (newVal) {
this.selectedStall = newVal
}
}
}
},
methods: {
getStalls: async function () {
try {
const {data} = await LNbits.api.request(
'GET',
'/nostrmarket/api/v1/stall?pending=false',
this.inkey
)
this.stalls = data
} catch (error) {
LNbits.utils.notifyApiError(error)
}
},
getProducts: async function () {
try {
// Fetch products from all stalls
const allProducts = []
for (const stall of this.stalls) {
const {data} = await LNbits.api.request(
'GET',
`/nostrmarket/api/v1/stall/product/${stall.id}?pending=false`,
this.inkey
)
allProducts.push(...data)
}
this.products = allProducts
} catch (error) {
LNbits.utils.notifyApiError(error)
}
},
getPendingProducts: async function () {
try {
// Fetch pending products from all stalls
const allPending = []
for (const stall of this.stalls) {
const {data} = await LNbits.api.request(
'GET',
`/nostrmarket/api/v1/stall/product/${stall.id}?pending=true`,
this.inkey
)
allPending.push(...data)
}
this.pendingProducts = allPending
} catch (error) {
LNbits.utils.notifyApiError(error)
}
},
getStallName: function (stallId) {
const stall = this.stalls.find(s => s.id === stallId)
return stall ? stall.name : 'Unknown'
},
getStallCurrency: function (stallId) {
const stall = this.stalls.find(s => s.id === stallId)
return stall ? stall.currency : 'sat'
},
getStall: function (stallId) {
return this.stalls.find(s => s.id === stallId)
},
newEmptyProductData: function () {
return {
id: null,
stall_id: this.stalls.length ? this.stalls[0].id : null,
name: '',
categories: [],
images: [],
image: null,
price: 0,
quantity: 0,
config: {
description: '',
use_autoreply: false,
autoreply_message: ''
}
}
},
showNewProductDialog: function () {
this.productDialog.data = this.newEmptyProductData()
this.productDialog.showDialog = true
},
editProduct: function (product) {
this.productDialog.data = {...product, image: null}
if (!this.productDialog.data.config) {
this.productDialog.data.config = {description: ''}
}
this.productDialog.showDialog = true
},
sendProductFormData: async function () {
const data = {
stall_id: this.productDialog.data.stall_id,
id: this.productDialog.data.id,
name: this.productDialog.data.name,
images: this.productDialog.data.images || [],
price: this.productDialog.data.price,
quantity: this.productDialog.data.quantity,
categories: this.productDialog.data.categories || [],
config: this.productDialog.data.config
}
this.productDialog.showDialog = false
if (this.productDialog.data.id) {
data.pending = false
await this.updateProduct(data)
} else {
await this.createProduct(data)
}
},
createProduct: async function (payload) {
try {
const {data} = await LNbits.api.request(
'POST',
'/nostrmarket/api/v1/product',
this.adminkey,
payload
)
this.products.unshift(data)
this.$q.notify({
type: 'positive',
message: 'Product Created'
})
} catch (error) {
LNbits.utils.notifyApiError(error)
}
},
updateProduct: async function (product) {
try {
const {data} = await LNbits.api.request(
'PATCH',
'/nostrmarket/api/v1/product/' + product.id,
this.adminkey,
product
)
const index = this.products.findIndex(p => p.id === product.id)
if (index !== -1) {
this.products.splice(index, 1, data)
} else {
this.products.unshift(data)
}
this.$q.notify({
type: 'positive',
message: 'Product Updated'
})
} catch (error) {
LNbits.utils.notifyApiError(error)
}
},
deleteProduct: function (product) {
LNbits.utils
.confirmDialog(`Are you sure you want to delete "${product.name}"?`)
.onOk(async () => {
try {
await LNbits.api.request(
'DELETE',
'/nostrmarket/api/v1/product/' + product.id,
this.adminkey
)
this.products = this.products.filter(p => p.id !== product.id)
this.$q.notify({
type: 'positive',
message: 'Product Deleted'
})
} catch (error) {
LNbits.utils.notifyApiError(error)
}
})
},
toggleProductActive: async function (product) {
await this.updateProduct({...product, active: !product.active})
},
addProductImage: function () {
if (!this.productDialog.data.image) return
if (!this.productDialog.data.images) {
this.productDialog.data.images = []
}
this.productDialog.data.images.push(this.productDialog.data.image)
this.productDialog.data.image = null
},
removeProductImage: function (imageUrl) {
const index = this.productDialog.data.images.indexOf(imageUrl)
if (index !== -1) {
this.productDialog.data.images.splice(index, 1)
}
},
openSelectPendingProductDialog: async function () {
await this.getPendingProducts()
this.productDialog.showRestore = true
},
openRestoreProductDialog: function (pendingProduct) {
pendingProduct.pending = true
this.productDialog.data = {...pendingProduct, image: null}
this.productDialog.showDialog = true
},
shortLabel: function (value = '') {
if (value.length <= 44) return value
return value.substring(0, 40) + '...'
}
},
created: async function () {
await this.getStalls()
await this.getProducts()
}
})