Some checks failed
lint.yml / feat: per-event organizer name + reply-to on ticket emails (pull_request) Failing after 0s
Each organizer is different, so the sender identity of ticket emails is now per event rather than per instance: - `extra.organizer_name` → From display name "Organizer via <site title>" (site title alone when unset). The From address stays the instance mailbox — that is what DKIM signs — so this costs nothing in mail auth. - `extra.reply_to_email` → Reply-To; blank falls back to the email on the LNbits account that owns the event wallet; no header when neither exists. When a reply-to exists the body says "Questions? Reply to this email and it reaches <organizer>", and "Organizer: <name>" is listed with the ticket details. Both fields sit next to the existing per-event subject/body in the admin dialog. The organizer's own wording (in whatever language) remains `notification_body`. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EYwoAkZZmXMMmaBp4WGUBo
706 lines
22 KiB
JavaScript
706 lines
22 KiB
JavaScript
window.PageEvents = {
|
|
template: '#page-events',
|
|
data() {
|
|
return {
|
|
events: [],
|
|
tickets: [],
|
|
resendingTicketEmails: [],
|
|
currencies: [],
|
|
pendingEvents: [],
|
|
allUserEvents: [],
|
|
isAdmin: false,
|
|
republishing: false,
|
|
republishingMine: false,
|
|
settings: {
|
|
auto_approve: false
|
|
},
|
|
allUsersEventsTable: {
|
|
// Shown on the admin All Users' Events card. Includes the
|
|
// wallet owner (`wallet_user_id` resolved server-side) so
|
|
// cross-tenant rows are attributable to a user.
|
|
columns: [
|
|
{
|
|
name: 'wallet_user_id',
|
|
align: 'left',
|
|
label: 'Owner',
|
|
field: 'wallet_user_id'
|
|
},
|
|
{name: 'id', align: 'left', label: 'ID', field: 'id'},
|
|
{name: 'name', align: 'left', label: 'Name', field: 'name'},
|
|
{
|
|
name: 'event_start_date',
|
|
align: 'left',
|
|
label: 'Start date',
|
|
field: 'event_start_date'
|
|
},
|
|
{
|
|
name: 'event_end_date',
|
|
align: 'left',
|
|
label: 'End date',
|
|
field: 'event_end_date'
|
|
},
|
|
{
|
|
name: 'closing_date',
|
|
align: 'left',
|
|
label: 'Ticket close',
|
|
field: 'closing_date'
|
|
},
|
|
{
|
|
name: 'canceled',
|
|
align: 'left',
|
|
label: 'Canceled',
|
|
field: row => {
|
|
if (row.extra && row.extra.conditional && row.canceled) {
|
|
return 'Yes'
|
|
}
|
|
return 'No'
|
|
}
|
|
},
|
|
{name: 'status', align: 'left', label: 'Status', field: 'status'}
|
|
]
|
|
},
|
|
eventsTable: {
|
|
columns: [
|
|
{name: 'id', align: 'left', label: 'ID', field: 'id'},
|
|
{name: 'name', align: 'left', label: 'Name', field: 'name'},
|
|
{
|
|
name: 'event_start_date',
|
|
align: 'left',
|
|
label: 'Start date',
|
|
field: 'event_start_date',
|
|
format: val => this.formatEventDate(val)
|
|
},
|
|
{
|
|
name: 'event_end_date',
|
|
align: 'left',
|
|
label: 'End date',
|
|
field: 'event_end_date',
|
|
format: val => this.formatEventDate(val)
|
|
},
|
|
{
|
|
name: 'closing_date',
|
|
align: 'left',
|
|
label: 'Ticket close',
|
|
field: 'closing_date'
|
|
},
|
|
{
|
|
name: 'canceled',
|
|
align: 'left',
|
|
label: 'Canceled',
|
|
field: row => {
|
|
if (row.extra.conditional && row.canceled) {
|
|
return 'Yes'
|
|
}
|
|
return 'No'
|
|
}
|
|
},
|
|
{
|
|
name: 'price_per_ticket',
|
|
align: 'left',
|
|
label: 'Price',
|
|
field: row => {
|
|
if (this.isFiatCurrency(row.currency)) {
|
|
return LNbits.utils.formatCurrency(
|
|
row.price_per_ticket.toFixed(2),
|
|
row.currency
|
|
)
|
|
}
|
|
return row.price_per_ticket
|
|
}
|
|
},
|
|
{
|
|
name: 'amount_tickets',
|
|
align: 'left',
|
|
label: 'No tickets',
|
|
field: 'amount_tickets'
|
|
},
|
|
{
|
|
name: 'sold',
|
|
align: 'left',
|
|
label: 'Sold',
|
|
field: 'sold'
|
|
},
|
|
{name: 'info', align: 'left', label: 'Info', field: 'info'},
|
|
{name: 'banner', align: 'left', label: 'Banner', field: 'banner'},
|
|
{name: 'status', align: 'left', label: 'Status', field: 'status'}
|
|
],
|
|
pagination: {
|
|
rowsPerPage: 10
|
|
}
|
|
},
|
|
ticketsTable: {
|
|
columns: [
|
|
{name: 'event', align: 'left', label: 'Event', field: 'event'},
|
|
{name: 'name', align: 'left', label: 'Name', field: 'name'},
|
|
{name: 'email', align: 'left', label: 'Email', field: 'email'},
|
|
{
|
|
name: 'registered',
|
|
align: 'left',
|
|
label: 'Registered',
|
|
field: 'registered'
|
|
},
|
|
{
|
|
name: 'promo_code',
|
|
align: 'left',
|
|
label: 'Promo Code',
|
|
field: row => row.extra.applied_promo_code || ''
|
|
},
|
|
{name: 'id', align: 'left', label: 'ID', field: 'id'}
|
|
],
|
|
pagination: {
|
|
rowsPerPage: 10
|
|
}
|
|
},
|
|
// Rails an organizer can enable per event. Mirrors the webapp's
|
|
// CreateEventDialog; `fiat` is rendered disabled when the LNbits user
|
|
// has no fiat provider (see `hasFiatProvider`).
|
|
paymentMethodOptions: [
|
|
{
|
|
value: 'lightning',
|
|
label: 'Lightning',
|
|
hint: 'Pay with any Lightning wallet'
|
|
},
|
|
{
|
|
value: 'fiat',
|
|
label: 'Card',
|
|
hint: 'Card or bank through your configured fiat provider'
|
|
}
|
|
],
|
|
// Same list the webapp offers (src/modules/events/types/category.ts);
|
|
// published as NIP-52 `t` tags so both clients filter on one vocabulary.
|
|
categoryOptions: [
|
|
'concert',
|
|
'workshop',
|
|
'market',
|
|
'festival',
|
|
'exhibition',
|
|
'sport',
|
|
'theater',
|
|
'cinema',
|
|
'party',
|
|
'talk',
|
|
'conference',
|
|
'meetup',
|
|
'food',
|
|
'outdoor',
|
|
'kids',
|
|
'wellness',
|
|
'technology',
|
|
'art',
|
|
'music',
|
|
'dance',
|
|
'literature',
|
|
'comedy',
|
|
'charity',
|
|
'tradition',
|
|
'other'
|
|
].map(c => ({label: c.charAt(0).toUpperCase() + c.slice(1), value: c})),
|
|
formDialog: {
|
|
show: false,
|
|
data: {
|
|
currency: 'sats',
|
|
allow_fiat: false,
|
|
fiat_currency: 'GBP',
|
|
location: '',
|
|
categories: [],
|
|
extra: {
|
|
payment_methods: ['lightning'],
|
|
promo_codes: [],
|
|
organizer_name: '',
|
|
reply_to_email: '',
|
|
notification_subject: '',
|
|
notification_body: ''
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
computed: {
|
|
hasFiatProvider() {
|
|
return (this.g.user?.fiat_providers || []).length > 0
|
|
},
|
|
fiatProviderNames() {
|
|
return (this.g.user?.fiat_providers || [])
|
|
.map(p => p.charAt(0).toUpperCase() + p.slice(1))
|
|
.join(', ')
|
|
},
|
|
acceptsFiat() {
|
|
return (this.formDialog.data.extra?.payment_methods || []).includes(
|
|
'fiat'
|
|
)
|
|
},
|
|
isSatPrice() {
|
|
return !this.isFiatCurrency(this.formDialog.data.currency)
|
|
},
|
|
fiatCurrencyOptions() {
|
|
return this.currencies.filter(c => this.isFiatCurrency(c))
|
|
}
|
|
},
|
|
methods: {
|
|
isFiatCurrency(currency) {
|
|
return !['sat', 'sats'].includes((currency || '').toLowerCase())
|
|
},
|
|
getTickets() {
|
|
LNbits.api
|
|
.request(
|
|
'GET',
|
|
'/events/api/v1/tickets?all_wallets=true',
|
|
this.g.user.wallets[0].adminkey
|
|
)
|
|
.then(response => {
|
|
this.tickets = response.data.filter(e => e.paid)
|
|
})
|
|
},
|
|
deleteTicket(ticketId) {
|
|
const tickets = _.findWhere(this.tickets, {id: ticketId})
|
|
const wallet = _.findWhere(this.g.user.wallets, {id: tickets.wallet})
|
|
|
|
LNbits.utils
|
|
.confirmDialog('Are you sure you want to delete this ticket')
|
|
.onOk(() => {
|
|
LNbits.api
|
|
.request(
|
|
'DELETE',
|
|
'/events/api/v1/tickets/' + ticketId,
|
|
wallet.adminkey
|
|
)
|
|
.then(response => {
|
|
this.tickets = _.reject(this.tickets, function (obj) {
|
|
return obj.id == ticketId
|
|
})
|
|
})
|
|
.catch(LNbits.utils.notifyApiError)
|
|
})
|
|
},
|
|
resendTicketEmail(ticket) {
|
|
if (!ticket.paid || !ticket.email) return
|
|
const wallet = _.findWhere(this.g.user.wallets, {id: ticket.wallet})
|
|
if (!wallet) return
|
|
|
|
this.resendingTicketEmails.push(ticket.id)
|
|
LNbits.api
|
|
.request(
|
|
'POST',
|
|
'/events/api/v1/tickets/' + ticket.id + '/resend-email',
|
|
wallet.adminkey
|
|
)
|
|
.then(response => {
|
|
this.tickets = this.tickets.map(obj =>
|
|
obj.id === ticket.id ? response.data : obj
|
|
)
|
|
Quasar.Notify.create({
|
|
type: 'positive',
|
|
message: 'Ticket email resent.',
|
|
icon: null
|
|
})
|
|
})
|
|
.catch(LNbits.utils.notifyApiError)
|
|
.finally(() => {
|
|
this.resendingTicketEmails = this.resendingTicketEmails.filter(
|
|
ticketId => ticketId !== ticket.id
|
|
)
|
|
})
|
|
},
|
|
exportticketsCSV() {
|
|
LNbits.utils.exportCSV(this.ticketsTable.columns, this.tickets)
|
|
},
|
|
getEvents() {
|
|
LNbits.api
|
|
.request(
|
|
'GET',
|
|
'/events/api/v1/events?all_wallets=true',
|
|
this.g.user.wallets[0].inkey
|
|
)
|
|
.then(response => {
|
|
this.events = response.data
|
|
this.checkCanceledEvents()
|
|
})
|
|
|
|
// Admin probe: a 200 from /all means we're an LNbits admin.
|
|
LNbits.api
|
|
.request('GET', '/events/api/v1/events/all')
|
|
.then(response => {
|
|
this.isAdmin = true
|
|
const ownWalletIds = this.g.user.wallets.map(w => w.id)
|
|
this.allUserEvents = response.data.filter(
|
|
e => !ownWalletIds.includes(e.wallet)
|
|
)
|
|
})
|
|
.catch(() => {
|
|
this.isAdmin = false
|
|
this.allUserEvents = []
|
|
})
|
|
},
|
|
getSettings() {
|
|
LNbits.api
|
|
.request('GET', '/events/api/v1/events/settings')
|
|
.then(response => {
|
|
this.settings = response.data
|
|
})
|
|
.catch(() => {
|
|
// Not admin or settings unavailable; keep defaults.
|
|
})
|
|
},
|
|
saveSettings() {
|
|
LNbits.api
|
|
.request('PUT', '/events/api/v1/events/settings', null, this.settings)
|
|
.then(() => {
|
|
Quasar.Notify.create({type: 'positive', message: 'Settings saved'})
|
|
})
|
|
.catch(LNbits.utils.notifyApiError)
|
|
},
|
|
getPendingEvents() {
|
|
LNbits.api
|
|
.request('GET', '/events/api/v1/events/pending')
|
|
.then(response => {
|
|
this.pendingEvents = response.data
|
|
})
|
|
.catch(() => {
|
|
this.pendingEvents = []
|
|
})
|
|
},
|
|
approveEvent(eventId) {
|
|
LNbits.utils.confirmDialog('Approve this event?').onOk(() => {
|
|
LNbits.api
|
|
.request('PUT', '/events/api/v1/events/' + eventId + '/approve')
|
|
.then(() => {
|
|
Quasar.Notify.create({
|
|
type: 'positive',
|
|
message: 'Event approved'
|
|
})
|
|
this.getEvents()
|
|
this.getPendingEvents()
|
|
})
|
|
.catch(LNbits.utils.notifyApiError)
|
|
})
|
|
},
|
|
rejectEvent(eventId) {
|
|
LNbits.utils.confirmDialog('Reject this event?').onOk(() => {
|
|
LNbits.api
|
|
.request('PUT', '/events/api/v1/events/' + eventId + '/reject')
|
|
.then(() => {
|
|
Quasar.Notify.create({
|
|
type: 'positive',
|
|
message: 'Event rejected'
|
|
})
|
|
this.getEvents()
|
|
this.getPendingEvents()
|
|
})
|
|
.catch(LNbits.utils.notifyApiError)
|
|
})
|
|
},
|
|
republishAllEvents() {
|
|
LNbits.utils
|
|
.confirmDialog(
|
|
'Re-emit every approved event to Nostr relays? This is safe ' +
|
|
'to run multiple times but generates one event per approved row.'
|
|
)
|
|
.onOk(() => {
|
|
this.republishing = true
|
|
LNbits.api
|
|
.request('POST', '/events/api/v1/events/republish-all')
|
|
.then(response => {
|
|
Quasar.Notify.create({
|
|
type: 'positive',
|
|
message:
|
|
'Republished ' +
|
|
response.data.republished +
|
|
' of ' +
|
|
response.data.total +
|
|
' events'
|
|
})
|
|
})
|
|
.catch(LNbits.utils.notifyApiError)
|
|
.finally(() => {
|
|
this.republishing = false
|
|
})
|
|
})
|
|
},
|
|
republishMyEvents() {
|
|
LNbits.utils
|
|
.confirmDialog('Re-emit your approved events to Nostr relays?')
|
|
.onOk(() => {
|
|
this.republishingMine = true
|
|
LNbits.api
|
|
.request(
|
|
'POST',
|
|
'/events/api/v1/events/republish-mine?all_wallets=true',
|
|
this.g.user.wallets[0].adminkey
|
|
)
|
|
.then(response => {
|
|
Quasar.Notify.create({
|
|
type: 'positive',
|
|
message:
|
|
'Republished ' +
|
|
response.data.republished +
|
|
' of your ' +
|
|
response.data.total +
|
|
' events'
|
|
})
|
|
})
|
|
.catch(LNbits.utils.notifyApiError)
|
|
.finally(() => {
|
|
this.republishingMine = false
|
|
})
|
|
})
|
|
},
|
|
foldDateTime(day, time) {
|
|
// Combine separate date/time inputs into the wire format
|
|
// expected by the events extension: "YYYY-MM-DD" or
|
|
// "YYYY-MM-DDTHH:MM" (time is optional).
|
|
if (!day) return null
|
|
return time ? `${day}T${time}` : day
|
|
},
|
|
splitDateTime(value) {
|
|
// Inverse of foldDateTime: split a stored string back into the
|
|
// day/time pieces the form inputs bind to. Slicing to HH:MM also
|
|
// drops the seconds + offset suffix withLocalTzOffset stamps on
|
|
// submit, so the organizer sees the wall-clock they entered.
|
|
if (!value) return {day: '', time: ''}
|
|
const [day, time = ''] = value.split('T')
|
|
// Time inputs only accept HH:MM, drop any seconds we stored.
|
|
return {day, time: time.slice(0, 5)}
|
|
},
|
|
withLocalTzOffset(value) {
|
|
// Stamp the browser's UTC offset on a "YYYY-MM-DDTHH:MM" value.
|
|
// The publisher's `_to_unix` treats a naive datetime as UTC, so an
|
|
// event entered as 18:00 in CEST would otherwise go out on Nostr
|
|
// as 18:00 UTC. Same transform the webapp applies; date-only
|
|
// values pass through unchanged (they map to NIP-52 kind 31922).
|
|
if (!value || !value.includes('T')) return value
|
|
const offMin = -new Date(value).getTimezoneOffset()
|
|
const sign = offMin >= 0 ? '+' : '-'
|
|
const abs = Math.abs(offMin)
|
|
const hh = String(Math.floor(abs / 60)).padStart(2, '0')
|
|
const mm = String(abs % 60).padStart(2, '0')
|
|
return `${value}:00${sign}${hh}:${mm}`
|
|
},
|
|
formatEventDate(value) {
|
|
// Table display: "YYYY-MM-DD" or "YYYY-MM-DD HH:MM".
|
|
if (!value) return ''
|
|
const {day, time} = this.splitDateTime(value)
|
|
return time ? `${day} ${time}` : day
|
|
},
|
|
validateEndDate() {
|
|
// Cross-field rule for the end-day input: end >= start, compared
|
|
// on the folded date+time so an equal-day earlier time is caught.
|
|
const d = this.formDialog.data
|
|
const start = this.foldDateTime(d.event_start_day, d.event_start_time)
|
|
const end = this.foldDateTime(d.event_end_day, d.event_end_time)
|
|
if (!start || !end) return true
|
|
return end >= start || 'End must be on or after start'
|
|
},
|
|
sendEventData() {
|
|
const wallet = _.findWhere(this.g.user.wallets, {
|
|
id: this.formDialog.data.wallet
|
|
})
|
|
const data = {...this.formDialog.data}
|
|
data.event_start_date = this.withLocalTzOffset(
|
|
this.foldDateTime(data.event_start_day, data.event_start_time)
|
|
)
|
|
data.event_end_date = this.withLocalTzOffset(
|
|
this.foldDateTime(data.event_end_day, data.event_end_time)
|
|
)
|
|
delete data.event_start_day
|
|
delete data.event_start_time
|
|
delete data.event_end_day
|
|
delete data.event_end_time
|
|
// Optional NIP-52 fields: blank location is "unset", not "".
|
|
data.location = (data.location || '').trim() || null
|
|
data.categories = data.categories || []
|
|
data.closing_date = data.closing_date || null
|
|
|
|
if (data.extra?.promo_codes) {
|
|
data.extra.promo_codes = data.extra.promo_codes
|
|
.filter(code => code.code?.trim() !== '')
|
|
.map(code => ({
|
|
...code,
|
|
code: code.code.trim().toUpperCase()
|
|
}))
|
|
}
|
|
const methods = data.extra?.payment_methods || []
|
|
if (methods.length === 0) {
|
|
Quasar.Notify.create({
|
|
type: 'warning',
|
|
message: 'Select at least one payment method.'
|
|
})
|
|
return
|
|
}
|
|
// allow_fiat stays the fiat-currency carrier the backend and the
|
|
// NIP-52 tags read; keep it in lockstep with the checkbox list.
|
|
data.allow_fiat = methods.includes('fiat')
|
|
if (this.isFiatCurrency(data.currency)) {
|
|
// A fiat-priced event settles in its price currency; mirror it so
|
|
// the payload (and the tickets_fiat_currency tag) stay coherent.
|
|
data.fiat_currency = data.currency
|
|
} else if (!data.allow_fiat) {
|
|
data.fiat_currency = 'GBP'
|
|
}
|
|
|
|
if (data.id) {
|
|
this.updateEvent(wallet, data)
|
|
} else {
|
|
this.createEvent(wallet, data)
|
|
}
|
|
},
|
|
|
|
openEventDialog(data = false) {
|
|
if (data && data.id) {
|
|
const start = this.splitDateTime(data.event_start_date)
|
|
const end = this.splitDateTime(data.event_end_date)
|
|
this.formDialog.data = {
|
|
...data,
|
|
extra: {
|
|
...(data.extra || {}),
|
|
// Events created before extra.payment_methods existed carry an
|
|
// empty list; show the rails the backend actually accepts for
|
|
// them (Lightning always, fiat when allow_fiat).
|
|
payment_methods: data.extra?.payment_methods?.length
|
|
? data.extra.payment_methods
|
|
: ['lightning', ...(data.allow_fiat ? ['fiat'] : [])]
|
|
},
|
|
location: data.location || '',
|
|
categories: [...(data.categories || [])],
|
|
event_start_day: start.day,
|
|
event_start_time: start.time,
|
|
event_end_day: end.day,
|
|
event_end_time: end.time
|
|
}
|
|
} else {
|
|
this.formDialog.data = {
|
|
currency: 'sats',
|
|
allow_fiat: false,
|
|
fiat_currency: 'GBP',
|
|
location: '',
|
|
categories: [],
|
|
event_start_day: '',
|
|
event_start_time: '',
|
|
event_end_day: '',
|
|
event_end_time: '',
|
|
extra: {
|
|
payment_methods: ['lightning'],
|
|
conditional: false,
|
|
min_tickets: 1,
|
|
email_notifications: false,
|
|
nostr_notifications: false,
|
|
promo_codes: [],
|
|
organizer_name: '',
|
|
reply_to_email: '',
|
|
notification_subject: '',
|
|
notification_body: ''
|
|
}
|
|
}
|
|
}
|
|
this.formDialog.show = true
|
|
},
|
|
resetEventDialog() {
|
|
this.formDialog.show = false
|
|
this.formDialog.data = {
|
|
currency: 'sats',
|
|
allow_fiat: false,
|
|
fiat_currency: 'GBP',
|
|
location: '',
|
|
categories: [],
|
|
extra: {
|
|
payment_methods: ['lightning'],
|
|
email_notifications: false,
|
|
nostr_notifications: false,
|
|
promo_codes: [],
|
|
organizer_name: '',
|
|
reply_to_email: '',
|
|
notification_subject: '',
|
|
notification_body: ''
|
|
}
|
|
}
|
|
},
|
|
|
|
createEvent(wallet, data) {
|
|
LNbits.api
|
|
.request('POST', '/events/api/v1/events', wallet.adminkey, data)
|
|
.then(response => {
|
|
this.events.push(response.data)
|
|
this.resetEventDialog()
|
|
})
|
|
.catch(LNbits.utils.notifyApiError)
|
|
},
|
|
updateformDialog(formId) {
|
|
const link = _.findWhere(this.events, {id: formId})
|
|
this.openEventDialog(link)
|
|
},
|
|
updateEvent(wallet, data) {
|
|
LNbits.api
|
|
.request(
|
|
'PUT',
|
|
'/events/api/v1/events/' + data.id,
|
|
wallet.adminkey,
|
|
data
|
|
)
|
|
.then(response => {
|
|
this.events = _.reject(this.events, function (obj) {
|
|
return obj.id == data.id
|
|
})
|
|
this.events.push(response.data)
|
|
this.resetEventDialog()
|
|
})
|
|
.catch(LNbits.utils.notifyApiError)
|
|
},
|
|
deleteEvent(eventsId) {
|
|
const events = _.findWhere(this.events, {id: eventsId})
|
|
|
|
LNbits.utils
|
|
.confirmDialog('Are you sure you want to delete this form link?')
|
|
.onOk(() => {
|
|
LNbits.api
|
|
.request(
|
|
'DELETE',
|
|
'/events/api/v1/events/' + eventsId,
|
|
_.findWhere(this.g.user.wallets, {id: events.wallet}).adminkey
|
|
)
|
|
.then(response => {
|
|
this.events = _.reject(this.events, function (obj) {
|
|
return obj.id == eventsId
|
|
})
|
|
})
|
|
.catch(LNbits.utils.notifyApiError)
|
|
})
|
|
},
|
|
exporteventsCSV() {
|
|
LNbits.utils.exportCSV(this.eventsTable.columns, this.events)
|
|
},
|
|
async checkCanceledEvents() {
|
|
const events = this.events
|
|
.filter(event => event.extra.conditional)
|
|
.filter(e => !e.canceled)
|
|
if (!events.length) return
|
|
const now = new Date()
|
|
events.forEach(async ev => {
|
|
if (new Date(ev.closing_date) < now && ev.sold < ev.extra.min_tickets) {
|
|
const {data} = await LNbits.api.request(
|
|
'PUT',
|
|
'/events/api/v1/events/' + ev.id + '/cancel',
|
|
_.findWhere(this.g.user.wallets, {id: ev.wallet}).adminkey
|
|
)
|
|
Quasar.Notify.create({
|
|
type: 'warning',
|
|
message: `Event ${ev.name} has been canceled and refunds have been issued.`,
|
|
icon: null
|
|
})
|
|
this.events = this.events.map(e => (e.id === ev.id ? data : e))
|
|
}
|
|
})
|
|
}
|
|
},
|
|
async created() {
|
|
if (this.g.user.wallets.length) {
|
|
this.getTickets()
|
|
this.getEvents()
|
|
this.getSettings()
|
|
this.getPendingEvents()
|
|
if (this.g.allowedCurrencies && this.g.allowedCurrencies.length > 0) {
|
|
this.currencies = ['sats', ...this.g.allowedCurrencies]
|
|
} else {
|
|
this.currencies = ['sats', ...this.g.currencies]
|
|
}
|
|
}
|
|
}
|
|
}
|