Merge upstream v1.6.8 into the aio fork
Brings in ticket waves (per-wave price/currency/stock/fiat), the paginated ticket endpoint, the organiser ticket-image template, and the SatsPay on-chain surface. Refs #33. Resolutions that were not mechanical, and why: - set_ticket_paid debits the wave named on the ticket, keeping upstream's `> 0` guards; ours decremented unconditionally and could go negative. The purchase and free-ticket paths now stamp ticket_wave_id / ticket_wave_title, without which every sale would debit the primary wave. - Pricing moved onto the selected wave (basket_totals takes it as a required argument, the promo-validate endpoint resolves the same wave through the shared _resolve_ticket_wave). event.price_per_ticket is a roll-up of the PRIMARY wave since sync_event_ticket_waves, so pricing off the event quoted and charged the first wave's price to buyers who picked a later one. Regression test added. - Kept npub support in two places upstream removed it: the purchase endpoint's normalize_public_key path and the notification dispatcher. Upstream's replacement rejects with "Only NIP-05 Nostr identifiers are supported", which is false for this fork. The purchase-side rejection had merged in outside any conflict marker. - _ticket_image_url existed on both sides as two unrelated features. Ours (always-attached rendered QR card) is now _ticket_card_url; upstream's (organiser template, opt-in per wave) keeps the name. Both are wired into the mail, and the /qr/{ticket_id} endpoint — also duplicated on both sides, on the same route — is merged into one handler rather than registered twice, where the second copy would have been unreachable. - models._parse_date now accepts a full ISO datetime. Upstream's date-only strptime raised ValueError on any event whose closing_date carries a time, which create_event produces by defaulting it from event_end_date — it would have 500'd the purchase path, the public event gate and the promo preview. Reproduced before fixing. - Dropped upstream's inline make_qr_png (we import a superset from .qr) and its duplicate paymentMethodOptions in display.js, which re-derived payment options from per-method booleans and offered an on-chain option the backend rejects; the submit gate now matches the template's condition. - Restored imports the merge silently dropped with upstream's npub removal (normalize_public_key, normalize_private_key, DEFAULT_NOSTR_RELAYS). Event create/update stays ours: upstream's combined endpoint would have replaced the approval workflow and the explicit field allowlist that keeps `status` out of the request body. mypy error set is unchanged from HEAD; ruff, black and the 97 tests pass.
This commit is contained in:
commit
ae5affa44f
14 changed files with 2228 additions and 275 deletions
|
|
@ -243,45 +243,102 @@
|
|||
<q-tr v-show="props.expand" :props="props">
|
||||
<q-td colspan="100%">
|
||||
<div class="q-pa-md">
|
||||
<div class="text-subtitle1 q-mb-md">Promo codes</div>
|
||||
<div class="row items-center q-mb-md">
|
||||
<div class="text-subtitle1">Ticket waves</div>
|
||||
<q-btn
|
||||
round
|
||||
dense
|
||||
unelevated
|
||||
color="primary"
|
||||
icon="add"
|
||||
class="q-ml-sm"
|
||||
@click="openTicketWaveDialog(props.row)"
|
||||
></q-btn>
|
||||
</div>
|
||||
<div class="column q-mb-lg">
|
||||
<div class="column">
|
||||
<div
|
||||
v-for="(wave, index) in props.row.extra.ticket_waves"
|
||||
:key="wave.id || index"
|
||||
class="q-mb-sm"
|
||||
>
|
||||
<q-chip
|
||||
square
|
||||
clickable
|
||||
class="q-py-xs"
|
||||
style="height: auto"
|
||||
@click="openTicketWaveDialog(props.row, wave)"
|
||||
>
|
||||
<span
|
||||
style="white-space: normal; line-height: 1.3"
|
||||
v-text="
|
||||
`${wave.title} - ${wave.opening_date} to ${
|
||||
wave.closing_date
|
||||
} - ${
|
||||
isFiatCurrency(wave.currency)
|
||||
? LNbits.utils.formatCurrency(
|
||||
Number(
|
||||
wave.price_per_ticket || 0
|
||||
).toFixed(2),
|
||||
wave.currency
|
||||
)
|
||||
: `${wave.price_per_ticket} sats`
|
||||
} - ${wave.amount_tickets} tickets - ${soldTicketsForWave(
|
||||
props.row.id,
|
||||
wave.id
|
||||
)} sold`
|
||||
"
|
||||
></span>
|
||||
</q-chip>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row items-center q-mb-md">
|
||||
<div class="text-subtitle1">Promo codes</div>
|
||||
<q-btn
|
||||
round
|
||||
dense
|
||||
unelevated
|
||||
color="primary"
|
||||
icon="edit"
|
||||
class="q-ml-sm"
|
||||
@click="openPromoCodesDialog(props.row)"
|
||||
></q-btn>
|
||||
</div>
|
||||
<div class="column">
|
||||
<div
|
||||
v-if="props.row.extra.promo_codes.length == 0"
|
||||
v-if="
|
||||
props.row.extra.promo_codes.filter(
|
||||
code => code.active
|
||||
).length == 0
|
||||
"
|
||||
class="text-caption"
|
||||
>
|
||||
No promo codes for this event.
|
||||
No active promo codes for this event.
|
||||
</div>
|
||||
<div
|
||||
v-for="(code, index) in props.row.extra.promo_codes"
|
||||
:key="index"
|
||||
class="row items-center q-col-gutter-sm q-mb-sm"
|
||||
>
|
||||
<div class="col-auto">
|
||||
<div class="row q-col-gutter-sm">
|
||||
<div
|
||||
v-for="(
|
||||
code, index
|
||||
) in props.row.extra.promo_codes.filter(
|
||||
code => code.active
|
||||
)"
|
||||
:key="index"
|
||||
class="col-auto q-mb-sm"
|
||||
>
|
||||
<q-chip
|
||||
square
|
||||
size="md"
|
||||
clickable
|
||||
@click="utils.copyText(code.code.toUpperCase())"
|
||||
>
|
||||
<q-avatar
|
||||
icon="bookmark"
|
||||
:color="code.active ? 'green' : 'grey'"
|
||||
text-color="white"
|
||||
></q-avatar>
|
||||
<span v-text="code.code.toUpperCase()"></span>
|
||||
<span
|
||||
v-text="
|
||||
`${code.code.toUpperCase()} - ${code.discount_percent}%`
|
||||
"
|
||||
></span>
|
||||
</q-chip>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
Discount:
|
||||
<span v-text="code.discount_percent"></span>%
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
Status:
|
||||
<span
|
||||
:class="code.active ? 'text-green' : 'text-grey'"
|
||||
v-text="code.active ? 'Active' : 'Inactive'"
|
||||
></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -308,9 +365,11 @@
|
|||
dense
|
||||
flat
|
||||
:rows="tickets"
|
||||
:loading="ticketsTable.loading"
|
||||
row-key="id"
|
||||
:columns="ticketsTable.columns"
|
||||
v-model:pagination="ticketsTable.pagination"
|
||||
@request="getTickets"
|
||||
>
|
||||
<template v-slot:header="props">
|
||||
<q-tr :props="props">
|
||||
|
|
@ -349,6 +408,18 @@
|
|||
>
|
||||
<q-tooltip>Resend ticket email</q-tooltip>
|
||||
</q-btn>
|
||||
<q-btn
|
||||
v-if="props.row.extra?.onchain && !props.row.paid"
|
||||
flat
|
||||
dense
|
||||
size="xs"
|
||||
@click="confirmOnchainTicket(props.row)"
|
||||
icon="currency_bitcoin"
|
||||
color="orange"
|
||||
class="q-ml-xs"
|
||||
>
|
||||
<q-tooltip>Confirm onchain payment</q-tooltip>
|
||||
</q-btn>
|
||||
</q-td>
|
||||
|
||||
<q-td v-for="col in props.cols" :key="col.name" :props="props">
|
||||
|
|
@ -597,6 +668,39 @@
|
|||
></q-input>
|
||||
</div>
|
||||
</div>
|
||||
<q-separator class="q-my-md"></q-separator>
|
||||
<div class="text-subtitle1 q-mt-lg q-mb-md">
|
||||
Primary ticket wave (you can add other waves later)
|
||||
</div>
|
||||
<div class="row q-col-gutter-sm">
|
||||
<div class="col-12 col-md-4">
|
||||
<q-input
|
||||
filled
|
||||
dense
|
||||
v-model.trim="primaryTicketWave().title"
|
||||
type="text"
|
||||
label="Wave title"
|
||||
></q-input>
|
||||
</div>
|
||||
<div class="col-12 col-md-4">
|
||||
<q-input
|
||||
filled
|
||||
dense
|
||||
v-model.trim="primaryTicketWave().opening_date"
|
||||
type="date"
|
||||
label="Ticket opening date"
|
||||
></q-input>
|
||||
</div>
|
||||
<div class="col-12 col-md-4">
|
||||
<q-input
|
||||
filled
|
||||
dense
|
||||
v-model.trim="formDialog.data.closing_date"
|
||||
type="date"
|
||||
label="Ticket closing date"
|
||||
></q-input>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row q-col-gutter-sm">
|
||||
<div class="col">
|
||||
<q-select
|
||||
|
|
@ -674,6 +778,49 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<q-toggle
|
||||
v-model="primaryTicketWave().use_ticket_image"
|
||||
label="Use ticket image"
|
||||
left-label
|
||||
></q-toggle>
|
||||
<div
|
||||
v-if="primaryTicketWave().use_ticket_image"
|
||||
class="row items-center q-col-gutter-sm q-mb-sm"
|
||||
>
|
||||
<div class="col-auto">
|
||||
<q-btn
|
||||
unelevated
|
||||
color="primary"
|
||||
type="a"
|
||||
:href="templateDownloadUrl()"
|
||||
download="ticket.jpg"
|
||||
>
|
||||
Download template
|
||||
<q-tooltip>400/733 jpg</q-tooltip>
|
||||
</q-btn>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<q-btn
|
||||
outline
|
||||
color="primary"
|
||||
:loading="isUploadingTicketTemplate"
|
||||
@click="triggerTicketImageUpload('primary')"
|
||||
>Replace</q-btn
|
||||
>
|
||||
</div>
|
||||
<div
|
||||
v-if="primaryTicketWave().ticket_image_id"
|
||||
class="col-12 text-caption"
|
||||
>
|
||||
Custom ticket template uploaded.
|
||||
</div>
|
||||
</div>
|
||||
<q-toggle
|
||||
v-model="formDialog.data.allow_fiat"
|
||||
label="Allow fiat checkout"
|
||||
left-label
|
||||
hint="Lets attendees pay through a configured fiat provider using the event currency."
|
||||
></q-toggle>
|
||||
<q-select
|
||||
v-if="acceptsFiat && isSatPrice"
|
||||
filled
|
||||
|
|
@ -718,135 +865,98 @@
|
|||
</div>
|
||||
</div>
|
||||
<q-separator class="q-my-md"></q-separator>
|
||||
<div class="text-subtitle1 q-mb-md">Promo Codes</div>
|
||||
<div class="text-caption">
|
||||
Allow users to enter a promo code for discounts.
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-for="(code, index) in formDialog.data.extra.promo_codes"
|
||||
:key="index"
|
||||
class="row q-col-gutter-sm q-mt-md"
|
||||
>
|
||||
<q-input
|
||||
class="col-6"
|
||||
filled
|
||||
dense
|
||||
v-model.trim="formDialog.data.extra.promo_codes[index].code"
|
||||
type="text"
|
||||
label="Promo Code"
|
||||
:hint="
|
||||
formDialog.data.extra.promo_codes[index].used_count
|
||||
? 'Used ' +
|
||||
formDialog.data.extra.promo_codes[index].used_count +
|
||||
(formDialog.data.extra.promo_codes[index].max_uses
|
||||
? ' / ' +
|
||||
formDialog.data.extra.promo_codes[index].max_uses
|
||||
: '')
|
||||
: ''
|
||||
"
|
||||
>
|
||||
<template v-slot:before>
|
||||
<q-checkbox
|
||||
left-label
|
||||
v-model="formDialog.data.extra.promo_codes[index].active"
|
||||
checked-icon="radio_button_checked"
|
||||
unchecked-icon="radio_button_unchecked"
|
||||
></q-checkbox>
|
||||
<q-tooltip>
|
||||
<span
|
||||
v-text="
|
||||
formDialog.data.extra.promo_codes[index].active
|
||||
? 'Active'
|
||||
: 'Inactive'
|
||||
"
|
||||
></span>
|
||||
</q-tooltip>
|
||||
</template>
|
||||
</q-input>
|
||||
<q-input
|
||||
class="col-3"
|
||||
filled
|
||||
dense
|
||||
v-model.number="
|
||||
formDialog.data.extra.promo_codes[index].discount_percent
|
||||
"
|
||||
type="number"
|
||||
label="Discount (%)"
|
||||
min="0"
|
||||
max="100"
|
||||
></q-input>
|
||||
<q-input
|
||||
class="col-3"
|
||||
filled
|
||||
dense
|
||||
v-model.number="
|
||||
formDialog.data.extra.promo_codes[index].max_uses
|
||||
"
|
||||
type="number"
|
||||
label="Max uses"
|
||||
placeholder="∞"
|
||||
min="1"
|
||||
hint="Blank = unlimited"
|
||||
>
|
||||
<template v-slot:after>
|
||||
<q-btn
|
||||
round
|
||||
dense
|
||||
flat
|
||||
icon="delete"
|
||||
@click="formDialog.data.extra.promo_codes.splice(index, 1)"
|
||||
></q-btn>
|
||||
</template>
|
||||
</q-input>
|
||||
</div>
|
||||
<div class="col-12 q-mt-md">
|
||||
<q-btn
|
||||
@click="
|
||||
formDialog.data.extra.promo_codes.push({
|
||||
code: '',
|
||||
discount_percent: 0,
|
||||
active: true,
|
||||
max_uses: null
|
||||
})
|
||||
"
|
||||
>Add Promo Code</q-btn
|
||||
>
|
||||
</div>
|
||||
<q-separator class="q-my-md"></q-separator>
|
||||
<div class="text-subtitle1 q-mb-md">Ticket Delivery</div>
|
||||
<div class="text-caption">
|
||||
Send the paid ticket link automatically by email or Nostr DM.
|
||||
</div>
|
||||
<q-toggle
|
||||
v-model="formDialog.data.extra.email_notifications"
|
||||
label="Email notifications"
|
||||
left-label
|
||||
></q-toggle>
|
||||
<q-toggle
|
||||
v-model="formDialog.data.extra.nostr_notifications"
|
||||
label="Nostr notifications"
|
||||
left-label
|
||||
></q-toggle>
|
||||
<div class="row items-center q-col-gutter-md">
|
||||
<div class="col-auto">
|
||||
<q-toggle
|
||||
v-model="formDialog.data.extra.email_notifications"
|
||||
label="Email notifications"
|
||||
left-label
|
||||
></q-toggle>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<q-toggle
|
||||
v-model="formDialog.data.extra.nostr_notifications"
|
||||
label="Nostr notifications"
|
||||
left-label
|
||||
></q-toggle>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-if="formDialog.data.extra.email_notifications"
|
||||
class="q-mt-md"
|
||||
>
|
||||
<q-input
|
||||
filled
|
||||
dense
|
||||
v-model.trim="formDialog.data.extra.notification_subject"
|
||||
type="text"
|
||||
label="Ticket notification subject"
|
||||
hint="Used as the email subject when sending paid ticket links."
|
||||
></q-input>
|
||||
<q-input
|
||||
class="q-mt-md"
|
||||
filled
|
||||
dense
|
||||
v-model.trim="formDialog.data.extra.notification_body"
|
||||
type="textarea"
|
||||
label="Ticket notification body"
|
||||
hint="Shown before the ticket link in the paid ticket notification."
|
||||
></q-input>
|
||||
</div>
|
||||
</q-expansion-item>
|
||||
|
||||
<q-separator class="q-my-md"></q-separator>
|
||||
<q-input
|
||||
filled
|
||||
dense
|
||||
v-model.trim="formDialog.data.extra.notification_subject"
|
||||
type="text"
|
||||
label="Ticket notification subject"
|
||||
hint="Used as the email subject when sending paid ticket links."
|
||||
></q-input>
|
||||
<q-input
|
||||
filled
|
||||
dense
|
||||
v-model.trim="formDialog.data.extra.notification_body"
|
||||
type="textarea"
|
||||
label="Ticket notification body"
|
||||
hint="Shown before the ticket link in the paid ticket notification."
|
||||
></q-input>
|
||||
<q-expansion-item
|
||||
group="advanced"
|
||||
icon="view_in_ar"
|
||||
label="Onchain payments"
|
||||
>
|
||||
<div class="q-mt-lg">
|
||||
<div class="text-caption q-mb-md">
|
||||
Accept Bitcoin onchain payments. Requires the
|
||||
<strong>SatsPay</strong> and <strong>Watchonly</strong>
|
||||
extensions to be installed and enabled.
|
||||
</div>
|
||||
<q-toggle
|
||||
v-model="formDialog.data.extra.onchain_enabled"
|
||||
label="Accept onchain payments"
|
||||
left-label
|
||||
@update:model-value="val => val && loadOnchainWallets()"
|
||||
></q-toggle>
|
||||
<q-select
|
||||
v-if="formDialog.data.extra.onchain_enabled"
|
||||
filled
|
||||
dense
|
||||
class="q-mt-md"
|
||||
v-model="formDialog.data.extra.onchain_wallet_id"
|
||||
label="Watchonly wallet"
|
||||
:options="
|
||||
onchainWallets.map(w => ({
|
||||
label: w.title || w.id,
|
||||
value: w.id
|
||||
}))
|
||||
"
|
||||
emit-value
|
||||
map-options
|
||||
hint="Bitcoin watchonly wallet for receiving onchain payments"
|
||||
></q-select>
|
||||
<div v-if="formDialog.data.extra.onchain_enabled" class="q-mt-md">
|
||||
<q-toggle
|
||||
v-model="formDialog.data.extra.onchain_zeroconf"
|
||||
label="Zero-conf (accept unconfirmed transactions)"
|
||||
left-label
|
||||
></q-toggle>
|
||||
<q-toggle
|
||||
v-model="formDialog.data.extra.onchain_fasttrack"
|
||||
label="Fasttrack (treat pending as paid)"
|
||||
left-label
|
||||
></q-toggle>
|
||||
</div>
|
||||
</div>
|
||||
</q-expansion-item>
|
||||
|
||||
<div class="row q-mt-lg">
|
||||
<q-btn
|
||||
|
|
@ -863,7 +973,9 @@
|
|||
:disable="
|
||||
!formDialog.data.wallet ||
|
||||
!formDialog.data.name ||
|
||||
!formDialog.data.event_start_day
|
||||
!formDialog.data.event_start_day ||
|
||||
primaryTicketWave().title == null ||
|
||||
primaryTicketWave().opening_date == null
|
||||
"
|
||||
type="submit"
|
||||
>Create Event</q-btn
|
||||
|
|
@ -875,5 +987,275 @@
|
|||
</q-form>
|
||||
</q-card>
|
||||
</q-dialog>
|
||||
|
||||
<q-dialog v-model="promoCodesDialog.show" position="top">
|
||||
<q-card class="q-pa-lg q-pt-xl lnbits__dialog-card">
|
||||
<q-form @submit="savePromoCodes" class="q-gutter-md">
|
||||
<div class="text-subtitle1">Promo Codes</div>
|
||||
<div class="text-caption">
|
||||
Allow users to enter a promo code for discounts.
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-for="(code, index) in promoCodesDialog.data.extra.promo_codes"
|
||||
:key="index"
|
||||
class="row q-col-gutter-sm q-mt-md"
|
||||
>
|
||||
<q-input
|
||||
class="col-8"
|
||||
filled
|
||||
dense
|
||||
v-model.trim="promoCodesDialog.data.extra.promo_codes[index].code"
|
||||
type="text"
|
||||
label="Promo Code"
|
||||
>
|
||||
<template v-slot:before>
|
||||
<q-checkbox
|
||||
left-label
|
||||
v-model="
|
||||
promoCodesDialog.data.extra.promo_codes[index].active
|
||||
"
|
||||
checked-icon="radio_button_checked"
|
||||
unchecked-icon="radio_button_unchecked"
|
||||
></q-checkbox>
|
||||
<q-tooltip>
|
||||
<span
|
||||
v-text="
|
||||
promoCodesDialog.data.extra.promo_codes[index].active
|
||||
? 'Active'
|
||||
: 'Inactive'
|
||||
"
|
||||
></span>
|
||||
</q-tooltip>
|
||||
</template>
|
||||
</q-input>
|
||||
<q-input
|
||||
class="col-2"
|
||||
filled
|
||||
dense
|
||||
v-model.number="
|
||||
promoCodesDialog.data.extra.promo_codes[index].discount_percent
|
||||
"
|
||||
type="number"
|
||||
label="Discount (%)"
|
||||
:hint="
|
||||
promoCodesDialog.data.extra.promo_codes[index].used_count
|
||||
? 'Used ' +
|
||||
promoCodesDialog.data.extra.promo_codes[index].used_count +
|
||||
(promoCodesDialog.data.extra.promo_codes[index].max_uses
|
||||
? ' / ' +
|
||||
promoCodesDialog.data.extra.promo_codes[index].max_uses
|
||||
: '')
|
||||
: ''
|
||||
"
|
||||
min="0"
|
||||
max="100"
|
||||
>
|
||||
<template v-slot:after>
|
||||
<q-btn
|
||||
round
|
||||
dense
|
||||
flat
|
||||
icon="delete"
|
||||
@click="
|
||||
promoCodesDialog.data.extra.promo_codes.splice(index, 1)
|
||||
"
|
||||
></q-btn>
|
||||
</template>
|
||||
</q-input>
|
||||
<q-input
|
||||
class="col-2"
|
||||
filled
|
||||
dense
|
||||
v-model.number="
|
||||
promoCodesDialog.data.extra.promo_codes[index].max_uses
|
||||
"
|
||||
type="number"
|
||||
label="Max uses"
|
||||
placeholder="∞"
|
||||
min="1"
|
||||
hint="Blank = unlimited"
|
||||
></q-input>
|
||||
</div>
|
||||
|
||||
<div class="col-12 q-mt-md">
|
||||
<q-btn @click="addPromoCodeToDialog">Add Promo Code</q-btn>
|
||||
</div>
|
||||
|
||||
<div class="row q-mt-lg">
|
||||
<q-btn unelevated color="primary" type="submit"
|
||||
>Save Promo Codes</q-btn
|
||||
>
|
||||
<q-btn
|
||||
flat
|
||||
color="grey"
|
||||
class="q-ml-auto"
|
||||
@click="resetPromoCodesDialog"
|
||||
>Cancel</q-btn
|
||||
>
|
||||
</div>
|
||||
</q-form>
|
||||
</q-card>
|
||||
</q-dialog>
|
||||
|
||||
<q-dialog v-model="ticketWaveDialog.show" position="top">
|
||||
<q-card class="q-pa-lg q-pt-xl lnbits__dialog-card">
|
||||
<q-form @submit="saveTicketWave" class="q-gutter-md">
|
||||
<div
|
||||
class="text-subtitle1"
|
||||
v-text="
|
||||
ticketWaveDialog.editingWaveId
|
||||
? 'Edit Ticket Wave'
|
||||
: 'Add Ticket Wave'
|
||||
"
|
||||
></div>
|
||||
<div class="row q-col-gutter-sm">
|
||||
<div class="col-12 col-md-4">
|
||||
<q-input
|
||||
filled
|
||||
dense
|
||||
v-model.trim="ticketWaveDialog.data.title"
|
||||
type="text"
|
||||
label="Wave title"
|
||||
></q-input>
|
||||
</div>
|
||||
<div class="col-12 col-md-4">
|
||||
<q-input
|
||||
filled
|
||||
dense
|
||||
v-model.trim="ticketWaveDialog.data.opening_date"
|
||||
type="date"
|
||||
label="Ticket opening date"
|
||||
></q-input>
|
||||
</div>
|
||||
<div class="col-12 col-md-4">
|
||||
<q-input
|
||||
filled
|
||||
dense
|
||||
v-model.trim="ticketWaveDialog.data.closing_date"
|
||||
type="date"
|
||||
label="Ticket closing date"
|
||||
></q-input>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row q-col-gutter-sm">
|
||||
<div class="col">
|
||||
<q-select
|
||||
filled
|
||||
dense
|
||||
v-model="ticketWaveDialog.data.currency"
|
||||
type="text"
|
||||
label="Unit"
|
||||
:options="currencies"
|
||||
></q-select>
|
||||
</div>
|
||||
<div class="col">
|
||||
<q-input
|
||||
filled
|
||||
dense
|
||||
v-model.number="ticketWaveDialog.data.amount_tickets"
|
||||
type="number"
|
||||
label="Amount of tickets"
|
||||
></q-input>
|
||||
</div>
|
||||
<div class="col">
|
||||
<q-input
|
||||
filled
|
||||
dense
|
||||
v-model.number="ticketWaveDialog.data.price_per_ticket"
|
||||
type="number"
|
||||
:label="'Price (' + ticketWaveDialog.data.currency + ') *'"
|
||||
:step="ticketWaveDialog.data.currency != 'sats' ? '0.01' : '1'"
|
||||
:mask="ticketWaveDialog.data.currency != 'sats' ? '#.##' : '#'"
|
||||
fill-mask="0"
|
||||
reverse-fill-mask
|
||||
:disable="ticketWaveDialog.data.currency == null"
|
||||
></q-input>
|
||||
</div>
|
||||
</div>
|
||||
<q-toggle
|
||||
v-model="ticketWaveDialog.data.use_ticket_image"
|
||||
label="Use ticket image"
|
||||
left-label
|
||||
></q-toggle>
|
||||
<div
|
||||
v-if="ticketWaveDialog.data.use_ticket_image"
|
||||
class="row items-center q-col-gutter-sm q-mb-sm"
|
||||
>
|
||||
<div class="col-auto">
|
||||
<q-btn
|
||||
unelevated
|
||||
color="primary"
|
||||
type="a"
|
||||
:href="templateDownloadUrl()"
|
||||
download="ticket.jpg"
|
||||
>
|
||||
Download template
|
||||
<q-tooltip>400/733 jpg</q-tooltip>
|
||||
</q-btn>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<q-btn
|
||||
outline
|
||||
color="primary"
|
||||
:loading="isUploadingTicketTemplate"
|
||||
@click="triggerTicketImageUpload('dialog')"
|
||||
>Replace</q-btn
|
||||
>
|
||||
</div>
|
||||
<div
|
||||
v-if="ticketWaveDialog.data.ticket_image_id"
|
||||
class="col-12 text-caption"
|
||||
>
|
||||
Custom ticket template uploaded.
|
||||
</div>
|
||||
</div>
|
||||
<q-toggle
|
||||
v-model="ticketWaveDialog.data.allow_fiat"
|
||||
label="Allow fiat checkout"
|
||||
left-label
|
||||
hint="Lets attendees pay through a configured fiat provider using this wave currency."
|
||||
></q-toggle>
|
||||
<q-select
|
||||
v-if="
|
||||
ticketWaveDialog.data.allow_fiat &&
|
||||
['sat', 'sats'].includes(
|
||||
(ticketWaveDialog.data.currency || '').toLowerCase()
|
||||
)
|
||||
"
|
||||
filled
|
||||
dense
|
||||
v-model="ticketWaveDialog.data.fiat_currency"
|
||||
label="Fiat checkout currency"
|
||||
:options="
|
||||
currencies.filter(
|
||||
c => !['sat', 'sats'].includes((c || '').toLowerCase())
|
||||
)
|
||||
"
|
||||
></q-select>
|
||||
<div class="row q-mt-lg">
|
||||
<q-btn unelevated color="primary" type="submit">{{
|
||||
ticketWaveDialog.editingWaveId
|
||||
? 'Update Ticket Wave'
|
||||
: 'Save Ticket Wave'
|
||||
}}</q-btn>
|
||||
<q-btn
|
||||
flat
|
||||
color="grey"
|
||||
class="q-ml-auto"
|
||||
@click="resetTicketWaveDialog"
|
||||
>Cancel</q-btn
|
||||
>
|
||||
</div>
|
||||
</q-form>
|
||||
</q-card>
|
||||
</q-dialog>
|
||||
<input
|
||||
ref="ticketImageUpload"
|
||||
type="file"
|
||||
accept="image/png,image/jpeg,image/webp"
|
||||
style="display: none"
|
||||
@change="handleTicketImageSelected"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue