Files
lnbits/lnbits/templates/components.vue
T
2025-11-12 14:30:27 +02:00

1780 lines
52 KiB
Vue

{% include('components/admin/funding.vue') %} {%
include('components/admin/funding_sources.vue') %} {%
include('components/admin/fiat_providers.vue') %} {%
include('components/admin/exchange_providers.vue') %} {%
include('components/admin/security.vue') %} {%
include('components/admin/users.vue') %} {%
include('components/admin/site_customisation.vue') %} {%
include('components/admin/audit.vue') %} {%
include('components/admin/extensions.vue') %} {%
include('components/admin/assets-config.vue') %} {%
include('components/admin/notifications.vue') %} {%
include('components/admin/server.vue') %} {%
include('components/new_user_wallet.vue') %} {%
include('components/lnbits-footer.vue') %} {%
include('components/lnbits-header.vue') %} {%
include('components/lnbits-drawer.vue') %} {%
include('components/lnbits-home-logos.vue') %}
<template id="lnbits-wallet-list">
<q-list
v-if="g.user && g.user.wallets.length"
dense
class="lnbits-drawer__q-list"
>
<q-item
v-for="walletRec in g.user.wallets.slice(
0,
g.user.extra.visible_wallet_count || 10
)"
:key="walletRec.id"
clickable
:active="g.wallet && g.wallet.id === walletRec.id"
@click="selectWallet(walletRec)"
>
<q-item-section side>
<q-avatar
size="lg"
:text-color="$q.dark.isActive ? 'black' : 'grey-3'"
:class="g.wallet && g.wallet.id === walletRec.id ? '' : 'disabled'"
:color="
g.wallet && g.wallet.id === walletRec.id
? walletRec.extra.color
: walletRec.extra.color
"
:icon="
g.wallet && g.wallet.id === walletRec.id
? walletRec.extra.icon
: walletRec.extra.icon
"
>
</q-avatar>
</q-item-section>
<q-item-section
style="max-width: 100px"
class="q-my-none ellipsis full-width"
>
<q-item-label lines="1"
><span v-text="walletRec.name"></span
></q-item-label>
<q-item-label class="q-my-none ellipsis full-width" caption>
<strong v-text="formatBalance(walletRec.sat)"></strong>
</q-item-label>
</q-item-section>
<q-item-section
v-if="walletRec.walletType == 'lightning-shared'"
side
top
>
<q-icon name="group" :color="walletRec.extra.color" size="xs"></q-icon>
</q-item-section>
</q-item>
<q-item
v-if="g.user.hiddenWalletsCount > 0"
clickable
@click="goToWallets()"
>
<q-item-section side>
<q-icon name="more_horiz" color="grey-5" size="md"></q-icon>
</q-item-section>
<q-item-section>
<q-item-label
lines="1"
class="text-caption"
v-text="$t('more_count', {count: g.user.hiddenWalletsCount})"
></q-item-label>
</q-item-section>
</q-item>
<q-item clickable @click="createWallet()">
<q-item-section side>
<q-icon name="add" color="grey-5" size="md"></q-icon>
</q-item-section>
<q-item-section>
<q-item-label
lines="1"
class="text-caption"
v-text="$t('add_new_wallet')"
></q-item-label>
<q-item-section v-if="g.user.walletInvitesCount" side>
<q-badge>
<span
v-text="'Wallet Invite (' + g.user.walletInvitesCount + ')'"
></span>
</q-badge>
</q-item-section>
</q-item-section>
</q-item>
</q-list>
</template>
<template id="lnbits-extension-list">
<q-list
v-if="
(g.user && userExtensions && userExtensions.length > 0) || !!searchTerm
"
dense
class="lnbits-drawer__q-list"
>
<q-item>
<q-item-section>
<q-input
v-model="searchTerm"
dense
borderless
:label="$t('extensions')"
>
</q-input>
</q-item-section>
</q-item>
<q-item
v-for="extension in userExtensions"
:key="extension.code"
clickable
:active="extension.isActive"
tag="a"
:href="extension.url"
>
<q-item-section side>
<q-avatar size="md">
<q-img :src="extension.tile" style="max-width: 20px"></q-img>
</q-avatar>
</q-item-section>
<q-item-section>
<q-item-label lines="1"
><span v-text="extension.name"></span>
</q-item-label>
</q-item-section>
<q-item-section side v-show="extension.isActive">
<q-icon name="chevron_right" color="grey-5" size="md"></q-icon>
</q-item-section>
</q-item>
<div class="lt-md q-mt-xl q-mb-xl"></div>
</q-list>
</template>
<template id="lnbits-manage">
<q-list v-if="g.user" dense class="lnbits-drawer__q-list">
<q-item-label header v-text="$t('manage')"></q-item-label>
<div v-if="g.user.admin">
<q-item v-if="showAdmin" to="/admin">
<q-item-section side>
<q-icon
name="settings"
:color="isActive('/admin') ? 'primary' : 'grey-5'"
size="md"
></q-icon>
</q-item-section>
<q-item-section>
<q-item-label lines="1" v-text="$t('settings')"></q-item-label>
</q-item-section>
</q-item>
<q-item v-if="showNode" to="/node">
<q-item-section side>
<q-icon
name="developer_board"
:color="isActive('/node') ? 'primary' : 'grey-5'"
size="md"
></q-icon>
</q-item-section>
<q-item-section>
<q-item-label lines="1" v-text="$t('node')"></q-item-label>
</q-item-section>
</q-item>
<q-item v-if="showUsers" to="/users">
<q-item-section side>
<q-icon
name="groups"
:color="isActive('/users') ? 'primary' : 'grey-5'"
size="md"
></q-icon>
</q-item-section>
<q-item-section>
<q-item-label lines="1" v-text="$t('users')"></q-item-label>
</q-item-section>
</q-item>
<q-item v-if="showAudit" to="/audit">
<q-item-section side>
<q-icon
name="playlist_add_check_circle"
:color="isActive('/audit') ? 'primary' : 'grey-5'"
size="md"
></q-icon>
</q-item-section>
<q-item-section>
<q-item-label lines="1" v-text="$t('api_watch')"></q-item-label>
</q-item-section>
</q-item>
</div>
<q-item to="/payments">
<q-item-section side>
<q-icon
name="query_stats"
:color="isActive('/payments') ? 'primary' : 'grey-5'"
size="md"
></q-icon>
</q-item-section>
<q-item-section>
<q-item-label lines="1" v-text="$t('payments')"></q-item-label>
</q-item-section>
</q-item>
<q-item v-if="showExtensions" to="/extensions">
<q-item-section side>
<q-icon
name="extension"
:color="isActive('/extensions') ? 'primary' : 'grey-5'"
size="md"
></q-icon>
</q-item-section>
<q-item-section>
<q-item-label lines="1" v-text="$t('extensions')"></q-item-label>
</q-item-section>
</q-item>
</q-list>
</template>
<template id="lnbits-payment-details">
<q-list separator>
<q-item v-if="payment.tag">
<q-item-section>
<q-item-label v-text="$t('created')"></q-item-label>
<q-item-label caption v-text="payment.date"></q-item-label>
</q-item-section>
<q-item-section side top>
<q-item-label caption v-text="payment.dateFrom"></q-item-label>
</q-item-section>
</q-item>
<q-item v-if="payment.expirydate">
<q-item-section>
<q-item-label v-text="$t('expiry')"></q-item-label>
<q-item-label caption v-text="payment.expirydate"></q-item-label>
</q-item-section>
<q-item-section side top>
<q-item-label caption v-text="payment.expirydateFrom"></q-item-label>
</q-item-section>
</q-item>
<q-item>
<q-item-section>
<q-item-label v-text="$t('amount')"></q-item-label>
<q-item-label caption>
<span v-text="(payment.amount / 1000).toFixed(3)"></span>
<span v-text="LNBITS_DENOMINATION"></span>
</q-item-label>
</q-item-section>
</q-item>
<q-item>
<q-item-section>
<q-item-label v-text="$t('fee')"></q-item-label>
<q-item-label caption>
<span v-text="(payment.fee / 1000).toFixed(3)"></span>
<span v-text="LNBITS_DENOMINATION"></span>
</q-item-label>
</q-item-section>
</q-item>
<q-item>
<q-item-section>
<q-item-label v-text="$t('payment_hash')"></q-item-label>
<q-item-label
caption
v-text="
`${payment.payment_hash.slice(0, 12)}...${payment.payment_hash.slice(-12)}`
"
></q-item-label>
</q-item-section>
<q-item-section side>
<q-item-label>
<q-icon
name="content_copy"
@click="copyText(payment.payment_hash)"
size="1em"
color="grey"
class="cursor-pointer"
/>
</q-item-label>
<q-tooltip>
<span v-text="payment.payment_hash"></span>
</q-tooltip>
</q-item-section>
</q-item>
<q-item>
<q-item-section>
<q-item-label v-text="$t('Invoice')"></q-item-label>
<q-item-label
caption
v-text="
`${payment.bolt11.slice(0, 12)}...${payment.bolt11.slice(-12)}`
"
></q-item-label>
</q-item-section>
<q-item-section side>
<q-item-label>
<q-icon
name="content_copy"
@click="copyText(payment.bolt11)"
size="1em"
color="grey"
class="cursor-pointer"
/>
</q-item-label>
<q-tooltip>
<span v-text="payment.bolt11"></span>
</q-tooltip>
</q-item-section>
</q-item>
<q-item>
<q-item-section>
<q-item-label v-text="$t('memo')"></q-item-label>
<q-item-label caption v-text="payment.memo"></q-item-label>
</q-item-section>
</q-item>
<q-item v-if="payment.webhook">
<q-item-section>
<q-item-label v-text="$t('webhook')"></q-item-label>
<q-item-label caption v-text="payment.webhook"></q-item-label>
</q-item-section>
<q-item-section side top>
<q-item-label caption>
<q-badge
:color="webhookStatusColor"
text-color="white"
v-text="webhookStatusText"
></q-badge>
</q-item-label>
</q-item-section>
</q-item>
<q-item v-if="payment.preimage">
<q-item-section>
<q-item-label v-text="$t('payment_proof')"></q-item-label>
<q-item-label
caption
v-text="
`${payment.preimage.slice(0, 12)}...${payment.preimage.slice(-12)}`
"
></q-item-label>
</q-item-section>
<q-item-section side>
<q-item-label>
<q-icon
name="content_copy"
@click="copyText(payment.preimage)"
size="1em"
color="grey"
class="cursor-pointer"
/>
</q-item-label>
<q-tooltip>
<span v-text="payment.preimage"></span>
</q-tooltip>
</q-item-section>
</q-item>
<q-expansion-item
expand-separator
icon="info"
header-class="text-grey-4"
label="Extras"
v-if="extras.length"
>
<template v-for="entry in extras">
<q-item
v-if="!!entry.value"
key="entry.key"
class="text-grey-4"
style="white-space: normal; word-break: break-all"
>
<q-item-section>
<q-item-label v-text="entry.key"></q-item-label>
<q-item-label caption v-text="entry.value"></q-item-label>
</q-item-section>
</q-item>
</template>
</q-expansion-item>
</q-list>
</template>
<template id="lnbits-dynamic-fields">
<div v-if="formData">
<div class="row q-mb-lg" v-for="o in options">
<div class="col auto-width">
<p v-if="o.options?.length" class="q-ml-xl">
<span v-text="o.label || o.name"></span>
<small v-if="o.description">
(<span v-text="o.description"></span>)</small
>
</p>
<lnbits-dynamic-fields
v-if="o.options?.length"
:options="o.options"
v-model="formData[o.name]"
@update:model-value="handleValueChanged"
class="q-ml-xl"
>
</lnbits-dynamic-fields>
<div v-else>
<q-input
v-if="o.type === 'number'"
type="number"
v-model="formData[o.name]"
@update:model-value="handleValueChanged"
:label="o.label || o.name"
:hint="o.description"
:rules="applyRules(o.required)"
filled
dense
></q-input>
<q-input
v-else-if="o.type === 'text'"
type="textarea"
rows="5"
v-model="formData[o.name]"
@update:model-value="handleValueChanged"
:label="o.label || o.name"
:hint="o.description"
:rules="applyRules(o.required)"
filled
dense
></q-input>
<q-input
v-else-if="o.type === 'password'"
v-model="formData[o.name]"
@update:model-value="handleValueChanged"
type="password"
:label="o.label || o.name"
:hint="o.description"
:rules="applyRules(o.required)"
filled
dense
></q-input>
<q-select
v-else-if="o.type === 'select'"
v-model="formData[o.name]"
@update:model-value="handleValueChanged"
:label="o.label || o.name"
:hint="o.description"
:options="o.values"
:rules="applyRules(o.required)"
></q-select>
<q-select
v-else-if="o.isList"
v-model.trim="formData[o.name]"
@update:model-value="handleValueChanged"
input-debounce="0"
new-value-mode="add-unique"
:label="o.label || o.name"
:hint="o.description"
:rules="applyRules(o.required)"
filled
multiple
dense
use-input
use-chips
hide-dropdown-icon
></q-select>
<div v-else-if="o.type === 'bool'">
<q-item tag="label" v-ripple>
<q-item-section avatar top>
<q-checkbox
v-model="formData[o.name]"
@update:model-value="handleValueChanged"
/>
</q-item-section>
<q-item-section>
<q-item-label
><span v-text="o.label || o.name"></span
></q-item-label>
<q-item-label caption>
<span v-text="o.description"></span>
</q-item-label>
</q-item-section>
</q-item>
</div>
<q-input
v-else-if="o.type === 'hidden'"
v-model="formData[o.name]"
type="text"
style="display: none"
:rules="applyRules(o.required)"
></q-input>
<div v-else-if="o.type === 'chips'">
<lnbits-dynamic-chips
v-model="formData[o.name]"
@update:model-value="handleValueChanged"
></lnbits-dynamic-chips>
</div>
<q-input
v-else
v-model="formData[o.name]"
@update:model-value="handleValueChanged"
:hint="o.description"
:label="o.label || o.name"
:rules="applyRules(o.required)"
filled
dense
></q-input>
</div>
</div>
</div>
</div>
</template>
<template id="lnbits-dynamic-chips">
<q-input
filled
v-model="chip"
@keydown.enter.prevent="addChip"
type="text"
label="wss://...."
hint="Add relays"
class="q-mb-md"
>
<q-btn @click="addChip" dense flat icon="add"></q-btn>
</q-input>
<div>
<q-chip
v-for="(chip, i) in chips"
:key="chip"
removable
@remove="removeChip(i)"
color="primary"
text-color="white"
:label="chip"
>
</q-chip>
</div>
</template>
<template id="lnbits-notifications-btn">
<q-btn
v-if="g.user.wallets"
:disabled="!this.isSupported"
dense
flat
round
@click="toggleNotifications()"
:icon="this.isSubscribed ? 'notifications_active' : 'notifications_off'"
size="sm"
type="a"
>
<q-tooltip v-if="this.isSupported && !this.isSubscribed"
>Subscribe to notifications</q-tooltip
>
<q-tooltip v-if="this.isSupported && this.isSubscribed"
>Unsubscribe from notifications</q-tooltip
>
<q-tooltip v-if="this.isSupported && this.isPermissionDenied">
Notifications are disabled,<br />please enable or reset permissions
</q-tooltip>
<q-tooltip v-if="!this.isSupported"
>Notifications are not supported</q-tooltip
>
</q-btn>
</template>
<template id="lnbits-update-balance">
<q-btn
v-if="admin && small_btn"
flat
round
color="primary"
size="sm"
icon="add"
>
<q-popup-edit class="text-white" v-slot="scope" v-model="credit">
<q-input
filled
:label="$t('credit_label', {denomination: denomination})"
v-model="scope.value"
dense
autofocus
@keyup.enter="updateBalance(scope)"
>
<template v-slot:append>
<q-icon name="edit" />
</template>
</q-input>
</q-popup-edit>
<q-tooltip v-text="$t('credit_hint')"></q-tooltip>
</q-btn>
<q-btn
v-if="admin && !small_btn"
color="primary"
:label="$t('credit_debit')"
class="float-right q-mt-sm"
size="sm"
>
<q-popup-edit class="text-white" v-slot="scope" v-model="credit">
<q-input
filled
:label="$t('credit_label', {denomination: denomination})"
v-model="scope.value"
type="number"
dense
autofocus
@keyup.enter="updateBalance(scope)"
>
<template v-slot:append>
<q-icon name="edit" />
</template>
</q-input>
</q-popup-edit>
<q-tooltip v-text="$t('credit_hint')"></q-tooltip>
</q-btn>
</template>
<template id="lnbits-qrcode">
<div
class="qrcode__outer"
:style="`margin: 13px auto; max-width: ${maxWidth}px`"
>
<div ref="qrWrapper" class="qrcode__wrapper">
<a
:href="href"
:title="href === '' ? value : href"
@click="clickQrCode"
class="no-link full-width"
>
<qrcode-vue
ref="qrCode"
:value="value"
:margin="margin"
:size="size"
level="Q"
render-as="svg"
class="rounded-borders q-mb-sm"
>
<q-tooltip :model-value="href === '' ? value : href"></q-tooltip>
</qrcode-vue>
</a>
<img
:src="logo"
class="qrcode__image"
alt="qrcode icon"
style="pointer-events: none"
/>
</div>
<div
v-if="showButtons"
class="qrcode__buttons row q-gutter-x-sm items-center justify-end no-wrap full-width"
>
<q-btn
v-if="nfc && nfcSupported"
:disabled="nfcTagWriting"
flat
dense
class="text-grey"
icon="nfc"
@click="writeNfcTag"
>
<q-tooltip>Write NFC Tag</q-tooltip>
</q-btn>
<q-btn flat dense class="text-grey" icon="download" @click="downloadSVG">
<q-tooltip>Download SVG</q-tooltip>
</q-btn>
<q-btn
flat
dense
class="text-grey"
@click="copyText(value)"
icon="content_copy"
>
<q-tooltip>Copy</q-tooltip>
</q-btn>
</div>
</div>
</template>
<template id="lnbits-qrcode-lnurl">
<div class="qrcode_lnurl__wrapper">
<q-tabs
v-model="tab"
dense
class="text-grey"
active-color="primary"
indicator-color="primary"
align="justify"
inline-label
>
<q-tab name="bech32" icon="qr_code" label="bech32"></q-tab>
<q-tab name="lud17" icon="link" label="url (lud17)"></q-tab>
</q-tabs>
<lnbits-qrcode :value="lnurl" nfc="true"></lnbits-qrcode>
</div>
</template>
<template id="lnbits-lnurlpay-success-action">
<div>
<p
class="q-mb-sm"
v-text="success_action.message || success_action.description"
></p>
<code
v-if="decryptedValue"
class="text-h6 q-mt-sm q-mb-none"
v-text="decryptedValue"
></code>
<p v-else-if="success_action.url" class="text-h6 q-mt-sm q-mb-none">
<a
target="_blank"
style="color: inherit"
:href="success_action.url"
v-text="success_action.url"
></a>
</p>
</div>
</template>
<template id="payment-list">
<div class="row items-center no-wrap">
<div class="col" v-if="!mobileSimple || $q.screen.gt.sm">
<q-input
:label="$t('search_by_tag_memo_amount')"
dense
class="q-pr-xl"
v-model="paymentsTable.search"
>
<template v-slot:before>
<q-icon name="search"> </q-icon>
</template>
<template v-slot:append>
<q-icon
v-if="paymentsTable.search !== ''"
name="close"
@click="paymentsTable.search = ''"
class="cursor-pointer"
>
</q-icon>
</template>
</q-input>
</div>
<div class="gt-sm col-auto">
<q-btn icon="event" flat color="grey">
<q-popup-proxy cover transition-show="scale" transition-hide="scale">
<q-date v-model="searchDate" mask="YYYY-MM-DD" range />
<div class="row">
<div class="col-6">
<q-btn
label="Search"
@click="searchByDate()"
color="primary"
flat
class="float-left"
v-close-popup
/>
</div>
<div class="col-6">
<q-btn
v-close-popup
@click="clearDateSeach()"
label="Clear"
class="float-right"
color="grey"
flat
/>
</div>
</div>
</q-popup-proxy>
<q-badge
v-if="searchDate?.to || searchDate?.from"
class="q-mt-lg q-mr-md"
color="primary"
rounded
floating
style="border-radius: 6px"
></q-badge>
<q-tooltip>
<span v-text="$t('filter_date')"></span>
</q-tooltip>
</q-btn>
<q-btn color="grey" icon="filter_alt" flat>
<q-menu>
<q-item dense>
<q-checkbox
v-model="searchStatus.success"
@click="handleFilterChanged"
label="Success Payments"
></q-checkbox>
</q-item>
<q-item dense>
<q-checkbox
v-model="searchStatus.pending"
@click="handleFilterChanged"
label="Pending Payments"
></q-checkbox>
</q-item>
<q-item dense>
<q-checkbox
v-model="searchStatus.failed"
@click="handleFilterChanged"
label="Failed Payments"
></q-checkbox>
</q-item>
<q-separator></q-separator>
<q-item dense>
<q-checkbox
v-model="searchStatus.incoming"
@click="handleFilterChanged"
label="Incoming Payments"
></q-checkbox>
</q-item>
<q-item dense>
<q-checkbox
v-model="searchStatus.outgoing"
@click="handleFilterChanged"
label="Outgoing Payments"
></q-checkbox>
</q-item>
</q-menu>
<q-tooltip>
<span v-text="$t('filter_payments')"></span>
</q-tooltip>
</q-btn>
<q-btn-dropdown
dense
outline
persistent
icon="archive"
split
class="q-mr-sm"
color="grey"
@click="exportCSV(false)"
>
<q-tooltip>
<span v-text="$t('export_csv')"></span>
</q-tooltip>
<q-list>
<q-item>
<q-item-section>
<q-input
@keydown.enter="addFilterTag"
filled
dense
v-model="exportTagName"
type="text"
label="Payment Tags"
class="q-pa-sm"
>
<q-btn @click="addFilterTag" dense flat icon="add"></q-btn>
</q-input>
</q-item-section>
</q-item>
<q-item v-if="exportPaymentTagList.length">
<q-item-section>
<div>
<q-chip
v-for="tag in exportPaymentTagList"
:key="tag"
removable
@remove="removeExportTag(tag)"
color="primary"
text-color="white"
:label="tag"
></q-chip>
</div>
</q-item-section>
</q-item>
<q-item>
<q-item-section>
<q-btn
v-close-popup
outline
color="grey"
@click="exportCSV(true)"
:label="$t('export_csv_details')"
></q-btn>
</q-item-section>
</q-item>
</q-list>
</q-btn-dropdown>
</div>
</div>
<div class="row q-my-md"></div>
<q-table
dense
flat
:rows="paymentsOmitter"
:row-key="paymentTableRowKey"
:columns="paymentsTable.columns"
:no-data-label="$t('no_transactions')"
:filter="paymentsTable.filter"
:loading="paymentsTable.loading"
:hide-header="mobileSimple"
:hide-bottom="mobileSimple"
v-model:pagination="paymentsTable.pagination"
@request="fetchPayments"
>
<template v-slot:header="props">
<q-tr :props="props" class="text-grey-5">
<q-th auto-width></q-th>
<q-th
v-for="col in props.cols"
:key="col.name"
:props="props"
v-text="col.label"
></q-th>
</q-tr>
</template>
<template v-slot:body="props">
<q-tr :props="props">
<q-td auto-width class="text-center cursor-pointer">
<q-icon
v-if="props.row.isPaid"
size="14px"
:name="props.row.isOut ? 'call_made' : 'call_received'"
:color="props.row.isOut ? 'pink' : 'green'"
@click="props.expand = !props.expand"
></q-icon>
<q-icon
v-else-if="props.row.isFailed"
name="warning"
color="yellow"
@click="props.expand = !props.expand"
>
<q-tooltip><span>failed</span></q-tooltip>
</q-icon>
<q-icon
v-else
name="downloading"
color="grey"
:style="
props.row.isOut
? 'transform: rotate(225deg)'
: 'transform: scaleX(-1) rotate(315deg)'
"
@click="props.expand = !props.expand"
>
<q-tooltip><span v-text="$t('pending')"></span></q-tooltip>
</q-icon>
</q-td>
<q-td
key="time"
:props="props"
style="white-space: normal; word-break: break-all"
>
<q-icon
v-if="
props.row.isIn &&
props.row.isPending &&
props.row.extra.hold_invoice
"
name="pause_presentation"
color="grey"
class="cursor-pointer q-mr-sm"
@click="showHoldInvoiceDialog(props.row)"
>
<q-tooltip><span v-text="$t('hold_invoice')"></span></q-tooltip>
</q-icon>
<q-badge
v-if="props.row.tag"
color="yellow"
text-color="black"
class="q-mr-sm"
>
<a
v-text="'#' + props.row.tag"
class="inherit"
:href="['/', props.row.tag].join('')"
></a>
</q-badge>
<span v-text="props.row.memo"></span>
<span
class="text-grey-5 q-ml-sm ellipsis"
v-if="props.row.extra.internal_memo"
v-text="`(${props.row.extra.internal_memo})`"
></span>
<br />
<i>
<span class="text-grey-5" v-text="props.row.dateFrom"></span>
<q-tooltip><span v-text="props.row.date"></span></q-tooltip>
</i>
</q-td>
<q-td
auto-width
key="amount"
v-if="denomination != 'sats'"
:props="props"
class="col1"
v-text="parseFloat(String(props.row.fsat).replaceAll(',', '')) / 100"
>
</q-td>
<q-td class="col2" auto-width key="amount" v-else :props="props">
<span v-text="props.row.fsat"></span>
<br />
<i v-if="props.row.extra.wallet_fiat_currency">
<span
v-text="
formatCurrency(
props.row.extra.wallet_fiat_amount,
props.row.extra.wallet_fiat_currency
)
"
></span>
<br />
</i>
<i v-if="props.row.extra.fiat_currency">
<span
v-text="
formatCurrency(
props.row.extra.fiat_amount,
props.row.extra.fiat_currency
)
"
></span>
</i>
</q-td>
<q-dialog v-model="props.expand" :props="props" position="top">
<q-card class="q-pa-sm q-pt-xl lnbits__dialog-card">
<q-card-section>
<q-list bordered separator>
<q-expansion-item
expand-separator
:default-opened="!(props.row.isIn && props.row.isPending)"
>
<template v-slot:header>
<q-item-section avatar>
<q-icon
:color="
props.row.isPaid && props.row.isIn
? 'green'
: props.row.isPaid && props.row.isOut
? 'pink'
: props.row.isFailed
? 'yellow'
: 'grey'
"
:name="
props.row.isPaid && props.row.isIn
? 'call_received'
: props.row.isPaid && props.row.isOut
? 'call_made'
: props.row.isFailed
? 'warning'
: 'settings_ethernet'
"
/>
</q-item-section>
<q-item-section>
<q-item-label
v-text="
props.row.isIn && props.row.isPending
? $t('invoice_waiting')
: props.row.isOut && props.row.isPending
? $t('outgoing_payment_pending')
: props.row.isPaid && props.row.isIn
? $t('payment_received')
: props.row.isPaid && props.row.isOut
? $t('payment_sent')
: props.row.isFailed
? $t('payment_failed')
: ''
"
></q-item-label>
</q-item-section>
<q-item-section v-if="props.row.tag" side>
<q-badge
v-if="props.row.extra && !!props.row.extra.tag"
color="yellow"
text-color="black"
>
#<span v-text="props.row.tag"></span>
</q-badge>
</q-item-section>
</template>
<q-separator></q-separator>
<lnbits-payment-details
:payment="props.row"
></lnbits-payment-details>
</q-expansion-item>
</q-list>
<div
v-if="props.row.isIn && props.row.isPending && props.row.bolt11"
>
<div v-if="props.row.extra.fiat_payment_request">
<lnbits-qrcode
:value="props.row.extra.fiat_payment_request"
:href="props.row.extra.fiat_payment_request"
:show-buttons="false"
></lnbits-qrcode>
</div>
<div v-else>
<lnbits-qrcode
:value="'lightning:' + props.row.bolt11.toUpperCase()"
:href="'lightning:' + props.row.bolt11"
></lnbits-qrcode>
</div>
</div>
<div class="row q-mt-md">
<q-btn
outline
color="grey"
@click="checkPayment(props.row.payment_hash)"
icon="refresh"
:label="$t('payment_check')"
></q-btn>
<q-btn
v-close-popup
flat
color="grey"
class="q-ml-auto"
:label="$t('close')"
></q-btn>
</div>
</q-card-section>
</q-card>
</q-dialog>
<q-dialog v-model="hodlInvoice.show" position="top">
<q-card class="q-pa-sm q-pt-xl lnbits__dialog-card">
<q-card-section>
<q-item-label class="text-h6">
<span v-text="$t('hold_invoice')"></span>
</q-item-label>
<q-item-label class="text-subtitle2">
<span v-text="$t('hold_invoice_description')"></span>
</q-item-label>
</q-card-section>
<q-card-section>
<q-input
filled
:label="$t('preimage')"
:hint="$t('preimage_hint')"
v-model="hodlInvoice.preimage"
dense
autofocus
@keyup.enter="settleHoldInvoice(hodlInvoice.preimage)"
>
</q-input>
</q-card-section>
<q-card-section class="row q-gutter-x-sm">
<q-btn
@click="settleHoldInvoice(hodlInvoice.preimage)"
outline
v-close-popup
color="grey"
:label="$t('settle_invoice')"
>
</q-btn>
<q-btn
v-close-popup
outline
color="grey"
class="q-ml-sm"
@click="cancelHoldInvoice(hodlInvoice.payment.payment_hash)"
:label="$t('cancel_invoice')"
></q-btn>
<q-btn
v-close-popup
flat
color="grey"
class="q-ml-auto"
:label="$t('close')"
></q-btn>
</q-card-section>
</q-card>
</q-dialog>
</q-tr>
</template>
</q-table>
</template>
<template id="lnbits-extension-rating">
<div style="margin-bottom: 3px">
<q-rating v-model="rating" size="1.5em" :max="5" color="primary"
><q-tooltip>
<span v-text="$t('extension_rating_soon')"></span> </q-tooltip
></q-rating>
</div>
</template>
<template id="lnbits-extension-settings-form">
<q-form v-if="settings" @submit="updateSettings" class="q-gutter-md">
<lnbits-dynamic-fields
:options="options"
v-model="settings"
></lnbits-dynamic-fields>
<div class="row q-mt-lg">
<q-btn v-close-popup unelevated color="primary" type="submit"
>Update</q-btn
>
<q-btn v-close-popup unelevated color="danger" @click="resetSettings"
>Reset</q-btn
>
<slot name="actions"></slot>
</div>
</q-form>
</template>
<template id="lnbits-extension-settings-btn-dialog">
<q-btn
v-if="options"
unelevated
@click="show = true"
color="primary"
icon="settings"
class="float-right"
>
<q-dialog v-model="show" position="top">
<q-card class="q-pa-lg q-pt-xl lnbits__dialog-card">
<lnbits-extension-settings-form
:options="options"
:adminkey="adminkey"
:endpoint="endpoint"
>
<template v-slot:actions>
<q-btn
v-close-popup
flat
color="grey"
class="q-ml-auto"
:label="$t('close')"
></q-btn>
</template>
</lnbits-extension-settings-form>
</q-card>
</q-dialog>
</q-btn>
</template>
<template id="user-id-only">
<div v-if="authAction === 'login' && authMethod === 'user-id-only'">
<q-card-section class="q-pb-none">
<div class="text-center text-h6">
<span v-text="$t('login_with_user_id')"></span>
</div>
</q-card-section>
<q-card-section>
<q-form @submit="loginUsr" class="q-gutter-md">
<q-input
dense
filled
v-model="user"
label="usr"
type="password"
></q-input>
<q-card-actions vertical align="center" class="q-pa-none">
<q-btn
color="primary"
:disable="user == ''"
type="submit"
:label="$t('login')"
class="full-width q-mb-sm"
></q-btn>
<q-btn
@click="showLogin('username-password')"
outline
color="grey"
:label="$t('back')"
class="full-width"
></q-btn>
</q-card-actions>
</q-form>
</q-card-section>
</div>
<div v-if="authAction === 'register' && authMethod === 'user-id-only'">
<q-card-section class="q-pb-none">
<div class="text-center text-h6">
<span v-text="$t('create_new_wallet')"></span>
</div>
</q-card-section>
<q-card-section>
<q-form @submit="createWallet" class="q-gutter-md">
<q-input
dense
filled
v-model="walletName"
:label="$t('name_your_wallet', {name: '{{ SITE_TITLE }} *'})"
></q-input>
<q-card-actions vertical align="center" class="q-pa-none">
<q-btn
color="primary"
:disable="walletName == ''"
type="submit"
:label="$t('add_new_wallet')"
class="full-width q-mb-sm"
></q-btn>
<q-btn
@click="showLogin('username-password')"
outline
color="grey"
:label="$t('back')"
class="full-width"
></q-btn>
</q-card-actions>
</q-form>
</q-card-section>
</div>
<q-card-section v-show="showInstantLogin">
<div>
<separator-text :text="$t('instant_access_question')"></separator-text>
<div class="text-body2 text-center q-mt-md">
<q-badge
@click="showLogin('user-id-only')"
color="primary"
class="cursor-pointer"
rounded
>
<strong>
<q-icon name="account_circle" size="xs"></q-icon>
<span v-text="$t('login_with_user_id')"></span> </strong
></q-badge>
<div v-if="allowed_new_users" class="inline-block">
<span v-text="$t('or')" class="q-mx-sm text-grey"></span>
<q-badge
@click="showRegister('user-id-only')"
color="primary"
class="cursor-pointer"
rounded
>
<strong>
<q-icon name="add" size="xs"></q-icon>
<span v-text="$t('create_new_wallet')"></span>
</strong>
</q-badge>
</div>
</div>
</div>
</q-card-section>
</template>
<template id="username-password">
<div v-if="authMethods.includes('username-password')">
<q-card-section class="q-pb-none">
<div class="text-center text-h6 q-mb-sm q-mt-none q-pt-none">
<span
v-if="authAction === 'login'"
v-text="$t('login_to_account')"
></span>
<span
v-if="authAction === 'register'"
v-text="$t('create_account')"
></span>
<span
v-if="authAction === 'reset'"
v-text="$t('reset_password')"
></span>
</div>
</q-card-section>
<!-- LOGIN -->
<q-card-section v-if="authAction === 'login'">
<q-form @submit="login" class="q-gutter-sm">
<q-input
dense
filled
v-model="username"
name="username"
:label="$t('username_or_email') + ' *'"
></q-input>
<q-input
dense
filled
v-model="password"
name="password"
:label="$t('password') + ' *'"
type="password"
></q-input>
<div class="row justify-end">
<q-btn
:disable="!username || !password"
color="primary"
type="submit"
:label="$t('login')"
class="full-width"
></q-btn>
</div>
</q-form>
</q-card-section>
<!-- REGISTER -->
<q-card-section v-if="allowed_new_users && authAction === 'register'">
<q-form @submit="register" class="q-gutter-sm">
<q-input
dense
filled
required
v-model="username"
:label="$t('username') + ' *'"
:rules="[val => validateUsername(val) || $t('invalid_username')]"
></q-input>
<q-input
dense
filled
v-model="password"
:label="$t('password') + ' *'"
type="password"
:rules="[val => !val || val.length >= 8 || $t('invalid_password')]"
></q-input>
<q-input
dense
filled
v-model="passwordRepeat"
:label="$t('password_repeat') + ' *'"
type="password"
:rules="[val => !val || val.length >= 8 || $t('invalid_password')]"
></q-input>
<div class="row justify-end">
<q-btn
unelevated
color="primary"
:disable="
!password ||
!passwordRepeat ||
!username ||
password !== passwordRepeat
"
type="submit"
class="full-width"
:label="$t('create_account')"
></q-btn>
</div>
</q-form>
</q-card-section>
<q-card-section v-else-if="!allowed_new_users && authAction === 'register'">
<h5 class="text-center" v-text="$t('new_user_not_allowed')"></h5>
</q-card-section>
<slot></slot>
<!-- RESET -->
<q-card-section v-if="authAction === 'reset'">
<q-form @submit="reset" class="q-gutter-sm">
<q-input
dense
filled
required
:disable="true"
v-model="reset_key"
:label="$t('reset_key') + ' *'"
></q-input>
<q-input
dense
filled
v-model="password"
:label="$t('password') + ' *'"
type="password"
:rules="[val => !val || val.length >= 8 || $t('invalid_password')]"
></q-input>
<q-input
dense
filled
v-model="passwordRepeat"
:label="$t('password_repeat') + ' *'"
type="password"
:rules="[val => !val || val.length >= 8 || $t('invalid_password')]"
></q-input>
<div class="row justify-end">
<q-btn
unelevated
color="primary"
:disable="
!password ||
!passwordRepeat ||
!reset_key ||
password !== passwordRepeat
"
type="submit"
class="full-width"
:label="$t('reset_password')"
></q-btn>
</div>
</q-form>
</q-card-section>
</div>
<!-- OAUTH -->
<q-card-section v-if="showOauth">
<div v-if="authMethods.includes('username-password')">
<separator-text :text="$t('signin_with_oauth_or')"></separator-text>
</div>
<q-card-section v-else class="q-pb-none">
<div class="text-center text-h6">
<span v-text="$t('signin_with_oauth')"></span>
</div>
</q-card-section>
<div class="flex justify-center q-mt-md" style="gap: 1rem">
<q-btn
v-if="authMethods.includes('nostr-auth-nip98')"
@click="signInWithNostr()"
outline
no-caps
color="grey"
padding="sm md"
>
<div class="row items-center no-wrap">
<q-avatar size="32px">
<q-img
class="bg-primary"
:src="`{{ static_url_for('static', 'images/logos/nostr.svg') }}`"
></q-img>
</q-avatar>
</div>
<q-tooltip>
<span v-text="$t('signin_with_nostr')"></span>
</q-tooltip>
</q-btn>
<q-btn
v-if="authMethods.includes('github-auth')"
href="/api/v1/auth/github"
type="a"
outline
no-caps
color="grey"
padding="sm md"
>
<div class="row items-center no-wrap">
<q-avatar size="32px">
<q-img
:src="`{{ static_url_for('static', 'images/github-logo.png') }}`"
:style="$q.dark.isActive ? 'filter: grayscale(1) invert(1)' : ''"
></q-img>
</q-avatar>
</div>
<q-tooltip><span v-text="$t('signin_with_github')"></span></q-tooltip>
</q-btn>
<q-btn
v-if="authMethods.includes('google-auth')"
href="/api/v1/auth/google"
type="a"
outline
no-caps
color="grey"
padding="sm md"
>
<div class="row items-center no-wrap">
<q-avatar size="32px">
<q-img
:src="`{{ static_url_for('static', 'images/google-logo.png') }}`"
></q-img>
</q-avatar>
</div>
<q-tooltip>
<span v-text="$t('signin_with_google')"></span>
</q-tooltip>
</q-btn>
<q-btn
v-if="authMethods.includes('keycloak-auth')"
href="/api/v1/auth/keycloak"
type="a"
outline
no-caps
color="grey"
class="btn-fixed-width"
>
<q-avatar size="32px" class="q-mr-md">
<q-img
:src="
keycloakIcon
? keycloakIcon
: `{{ static_url_for('static', 'images/keycloak-logo.png') }}`
"
></q-img>
</q-avatar>
<div>
<span
v-text="
$t('signin_with_custom_org', {
custom_org: keycloakOrg
})
"
></span>
</div>
</q-btn>
</div>
</q-card-section>
</template>
<template id="separator-text">
<div class="row fit no-wrap items-center">
<q-separator class="col q-mr-sm"></q-separator>
<div class="col-grow q-mx-sm">
<span :class="`text-h6 text-${color}`" v-text="text"></span>
</div>
<q-separator class="col q-ml-sm"></q-separator>
</div>
</template>
<template id="lnbits-data-fields">
<q-table
:rows="fields"
row-key="name"
:columns="fieldsTable.columns"
v-model:pagination="fieldsTable.pagination"
>
<template v-slot:bottom-row>
<q-tr>
<q-td auto-width></q-td>
<q-td colspan="100%">
<q-btn
@click="addField"
icon="add"
size="sm"
color="primary"
class="q-ml-xs"
:label="$t('add_field')"
/>
</q-td>
</q-tr>
</template>
<template v-slot:header="props">
<q-tr :props="props">
<q-th auto-width></q-th>
<q-th v-for="col in props.cols" :key="col.name" :props="props">
<span v-text="col.label"></span>
<q-icon
v-if="col.name == 'optional'"
name="info"
size="xs"
color="primary"
class="cursor-pointer q-ml-xs q-mb-xs"
>
<q-tooltip>
<ul>
<li>
The field is optional. The field can be left blank by the
user.
</li>
<li>
The UI form will not require this field to be filled out.
</li>
<li>The DB table will allow NULL values for this field.</li>
<li>Non optional fields must be filled out.</li>
</ul>
</q-tooltip>
</q-icon>
<q-icon
v-else-if="col.name == 'editable'"
name="info"
size="xs"
color="primary"
class="cursor-pointer q-ml-xs q-mb-xs"
>
<q-tooltip>
<ul>
<li>The UI form will allow the field to be edited.</li>
</ul>
</q-tooltip>
</q-icon>
<q-icon
v-else-if="col.name == 'sortable'"
name="info"
size="xs"
color="primary"
class="cursor-pointer q-ml-xs q-mb-xs"
>
<q-tooltip>
<ul>
<li>In the UI Table a column will be created for the field.</li>
<li>The UI Table column will be sortable.</li>
</ul>
</q-tooltip>
</q-icon>
<q-icon
v-else-if="col.name == 'searchable'"
name="info"
size="xs"
color="primary"
class="cursor-pointer q-ml-xs q-mb-xs"
>
<q-tooltip>
<ul>
<li>
The free text search will include this field when searching.
</li>
</ul>
</q-tooltip>
</q-icon>
</q-th>
</q-tr>
</template>
<template v-slot:body="props">
<q-tr :props="props">
<q-td>
<q-btn
v-if="props.row.readonly !== true"
@click="removeField(props.row)"
round
icon="delete"
size="sm"
color="negative"
class="q-ml-xs"
>
</q-btn>
</q-td>
<q-td full-width>
<q-input
dense
filled
v-model="props.row.name"
:readonly="props.row.readonly === true"
type="text"
>
</q-input>
</q-td>
<q-td>
<q-select
filled
dense
emit-value
map-options
v-model="props.row.type"
:options="fieldTypes"
:readonly="props.row.readonly === true"
></q-select>
</q-td>
<q-td>
<q-input dense filled v-model="props.row.label" type="text">
</q-input>
</q-td>
<q-td>
<q-input dense filled v-model="props.row.hint" type="text"> </q-input>
</q-td>
<q-td>
<q-toggle
v-model="props.row.optional"
:readonly="props.row.readonly === true"
size="md"
color="green"
/>
</q-td>
<q-td v-if="!hideAdvanced">
<q-toggle
v-if="props.row.type !== 'json'"
:readonly="props.row.readonly === true"
v-model="props.row.editable"
size="md"
color="green"
/>
</q-td>
<q-td v-if="!hideAdvanced">
<q-toggle
v-if="props.row.type !== 'json'"
:readonly="props.row.readonly === true"
v-model="props.row.sortable"
size="md"
color="green"
/>
</q-td>
<q-td v-if="!hideAdvanced">
<q-toggle
v-if="props.row.type !== 'json'"
:readonly="props.row.readonly === true"
v-model="props.row.searchable"
size="md"
color="green"
/>
</q-td>
</q-tr>
<q-tr v-if="props.row.type === 'json'" :props="props">
<q-td></q-td>
<q-td></q-td>
<q-td colspan="100%">
<lnbits-data-fields
:fields="props.row.fields"
:hide-advanced="true"
></lnbits-data-fields>
</q-td>
</q-tr>
</template>
</q-table>
</template>