nostrmarket/static/components/stall-details/stall-details.js
Vlad Stan 51c4147e65 Product delete (#64)
* feat: restore stalls from `nostr` as pending

* feat: stall and prod last update time

* feat: restore products and stalls as `pending`

* feat: show pending stalls

* feat: restore stall

* feat: restore a stall from nostr

* feat: add  blank `Restore Product` button

* fix: handle no talls to restore case

* feat: show restore dialog

* feat: allow query for pending products

* feat: restore products

* chore: code clean-up

* fix: last dm and last order query

* chore: code clean-up

* fix: subscribe for stalls and products on merchant create/restore

* feat: add message type to orders

* feat: simplify messages; code format

* feat: add type to DMs; restore DMs from nostr

* fix: parsing ints

* fix: hide copy button if invoice not present

* fix: do not generate invoice if product not found

* feat: order restore: first version

* refactor: move some logic into `services`

* feat: improve restore UX

* fix: too many calls to customer DMs

* fix: allow `All` customers filter

* fix: ws reconnect on server restart

* fix: query for customer profiles only one

* fix: unread messages per customer per merchant

* fix: disable `user-profile-events`

* fix: customer profile is optional

* fix: get customers after new message debounced

* chore: code clean-up

* feat: auto-create zone

* feat: fixed ID for default zone

* feat: notify order paid
2023-06-30 13:12:56 +03:00

330 lines
9.1 KiB
JavaScript

async function stallDetails(path) {
const template = await loadTemplateAsync(path)
const pica = window.pica()
Vue.component('stall-details', {
name: 'stall-details',
template,
props: [
'stall-id',
'adminkey',
'inkey',
'wallet-options',
'zone-options',
'currencies'
],
data: function () {
return {
tab: 'products',
stall: null,
products: [],
pendingProducts: [],
productDialog: {
showDialog: false,
showRestore: false,
url: true,
data: {
id: null,
name: '',
categories: [],
images: [],
image: null,
price: 0,
quantity: 0,
config: {
description: ''
}
}
},
productsFilter: '',
productsTable: {
columns: [
{
name: 'delete',
align: 'left',
label: '',
field: ''
},
{
name: 'edit',
align: 'left',
label: '',
field: ''
},
{
name: 'id',
align: 'left',
label: 'ID',
field: 'id'
},
{
name: 'name',
align: 'left',
label: 'Name',
field: 'name'
},
{
name: 'price',
align: 'left',
label: 'Price',
field: 'price'
},
{
name: 'quantity',
align: 'left',
label: 'Quantity',
field: 'quantity'
},
{
name: 'categories',
align: 'left',
label: 'Categories',
field: 'categories'
},
{
name: 'description',
align: 'left',
label: 'Description',
field: 'description'
}
],
pagination: {
rowsPerPage: 10
}
}
}
},
computed: {
filteredZoneOptions: function () {
if (!this.stall) return []
return this.zoneOptions.filter(z => z.currency === this.stall.currency)
}
},
methods: {
mapStall: function (stall) {
stall.shipping_zones.forEach(
z =>
(z.label = z.name
? `${z.name} (${z.countries.join(', ')})`
: z.countries.join(', '))
)
return stall
},
getStall: async function () {
try {
const { data } = await LNbits.api.request(
'GET',
'/nostrmarket/api/v1/stall/' + this.stallId,
this.inkey
)
this.stall = this.mapStall(data)
} catch (error) {
LNbits.utils.notifyApiError(error)
}
},
updateStall: async function () {
try {
const { data } = await LNbits.api.request(
'PUT',
'/nostrmarket/api/v1/stall/' + this.stallId,
this.adminkey,
this.stall
)
this.stall = this.mapStall(data)
this.$emit('stall-updated', this.stall)
this.$q.notify({
type: 'positive',
message: 'Stall Updated',
timeout: 5000
})
} catch (error) {
console.warn(error)
LNbits.utils.notifyApiError(error)
}
},
deleteStall: function () {
LNbits.utils
.confirmDialog(
`
Products and orders will be deleted also!
Are you sure you want to delete this stall?
`
)
.onOk(async () => {
try {
await LNbits.api.request(
'DELETE',
'/nostrmarket/api/v1/stall/' + this.stallId,
this.adminkey
)
this.$emit('stall-deleted', this.stallId)
this.$q.notify({
type: 'positive',
message: 'Stall Deleted',
timeout: 5000
})
} catch (error) {
console.warn(error)
LNbits.utils.notifyApiError(error)
}
})
},
addProductImage: function () {
if (!isValidImageUrl(this.productDialog.data.image)) {
this.$q.notify({
type: 'warning',
message: 'Not a valid image URL',
timeout: 5000
})
return
}
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)
}
},
getProducts: async function (pending = false) {
try {
const { data } = await LNbits.api.request(
'GET',
`/nostrmarket/api/v1/stall/product/${this.stall.id}?pending=${pending}`,
this.inkey
)
return data
} catch (error) {
LNbits.utils.notifyApiError(error)
}
},
sendProductFormData: function () {
var data = {
stall_id: this.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
this.updateProduct(data)
} else {
this.createProduct(data)
}
},
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(r => r.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',
timeout: 5000
})
} catch (error) {
console.warn(error)
LNbits.utils.notifyApiError(error)
}
},
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',
timeout: 5000
})
} catch (error) {
console.warn(error)
LNbits.utils.notifyApiError(error)
}
},
editProduct: async function (product) {
this.productDialog.data = { ...product }
this.productDialog.showDialog = true
},
deleteProduct: async function (productId) {
LNbits.utils
.confirmDialog('Are you sure you want to delete this product?')
.onOk(async () => {
try {
await LNbits.api.request(
'DELETE',
'/nostrmarket/api/v1/product/' + productId,
this.adminkey
)
this.products = _.reject(this.products, function (obj) {
return obj.id === productId
})
this.$q.notify({
type: 'positive',
message: 'Product deleted',
timeout: 5000
})
} catch (error) {
console.warn(error)
LNbits.utils.notifyApiError(error)
}
})
},
showNewProductDialog: async function (data) {
this.productDialog.data = data || {
id: null,
name: '',
description: '',
categories: [],
image: null,
images: [],
price: 0,
quantity: 0,
config: {
description: ''
}
}
this.productDialog.showDialog = true
},
openSelectPendingProductDialog: async function () {
this.productDialog.showRestore = true
this.pendingProducts = await this.getProducts(true)
},
openRestoreProductDialog: async function (pendingProduct) {
pendingProduct.pending = true
await this.showNewProductDialog(pendingProduct)
},
customerSelectedForOrder: function (customerPubkey) {
this.$emit('customer-selected-for-order', customerPubkey)
}
},
created: async function () {
await this.getStall()
this.products = await this.getProducts()
}
})
}