Boltcard view page (#12)

This commit is contained in:
Tiago Vasconcelos 2023-08-22 17:41:38 +01:00 committed by GitHub
commit a823cf3e5d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 186 additions and 1 deletions

View file

@ -0,0 +1,146 @@
{% extends "public.html" %} {% block toolbar_title %} Boltcard: ${
card.card_name }{% endblock %} {% block page %}
<div class="row q-col-gutter-md justify-center">
<div class="col-12 col-md-6 col-lg-5 q-gutter-y-md">
<q-card>
<q-card-section>
<div class="text-h6">Card Info</div>
<div class="text-subtitle2">This card is ${enabled}</div>
</q-card-section>
<q-separator dark inset></q-separator>
<q-card-section>
<q-list>
<q-item v-for="item in cardInfo">
<q-item-section>
<q-item-label lines="1">${item.label}</q-item-label>
<q-item-label caption>${card[item.value]}</q-item-label>
</q-item-section>
<q-item-section
side
v-if="!['tx_limit', 'daily_limit'].includes(item.value)"
>
<q-tooltip>Click to copy</q-tooltip>
<q-btn
flat
round
icon="content_copy"
@click="copyText(item.value)"
/>
</q-item-section>
</q-item>
</q-list>
</q-card-section>
</q-card>
</div>
<div class="col-12 col-md-6 col-lg-5 q-gutter-y-md">
<q-card>
<q-card-section>
<div class="text-h6">Transactions</div>
<div class="text-subtitle2">Latest transactions</div>
</q-card-section>
<q-separator dark inset></q-separator>
<q-card-section>
<q-list>
<q-item v-for="hit in hits">
<q-item-section avatar>
<q-icon
:name="hit.spent > 0 ? 'done_all' : 'done'"
:color="hit.spent > 0 ? 'green' : 'grey'"
/>
</q-item-section>
<q-item-section>
<q-item-label
>ID: ${hit.id} ${refunds.some(r => r.hit_id == hit.id) ?
'(Refunded)' : null}</q-item-label
>
<q-item-label caption lines="1">IP: ${hit.ip}</q-item-label>
</q-item-section>
<q-item-section side top>
<q-item-label caption>${hit.date}</q-item-label>
<q-item-label caption>${hit.amount} sats</q-item-label>
</q-item-section>
</q-item>
</q-list>
</q-card-section>
</q-card>
</div>
</div>
{% endblock %}{% block scripts %}
<script>
const mapHits = obj => {
obj.date = Quasar.utils.date.formatDate(
new Date(obj.time * 1000),
'YYYY-MM-DD HH:mm'
)
return obj
}
Vue.component(VueQrcode.name, VueQrcode)
new Vue({
el: '#vue',
delimiters: ['${', '}'],
mixins: [windowMixin],
data: function () {
return {
card: null,
hits: null,
cardInfo: [
{
label: 'Card UID',
value: 'uid'
},
{
label: 'Auth Key',
value: 'k0'
},
{
label: 'Meta Key',
value: 'k1'
},
{
label: 'File Key',
value: 'k2'
},
{
label: 'Transaction Limit',
value: 'tx_limit'
},
{
label: 'Daily Limit',
value: 'daily_limit'
}
]
}
},
created() {
this.card = JSON.parse('{{ card | tojson}}')
let hits = JSON.parse('{{ hits | tojson}}')
let refunds = JSON.parse('{{ refunds | tojson}}')
this.refunds = refunds || []
this.hits = hits.map(mapHits)
},
computed: {
enabled() {
return this.card.enable ? 'Enabled' : 'Disabled'
}
},
methods: {
copyText(text, message, position) {
Quasar.utils.copyToClipboard(text).then(() => {
this.$q.notify({
message: message || 'Copied to clipboard!',
position: position || 'bottom'
})
})
}
}
})
</script>
{% endblock %}

View file

@ -42,6 +42,7 @@
{% raw %}
<template v-slot:header="props">
<q-tr :props="props">
<q-th auto-width></q-th>
<q-th auto-width></q-th>
<q-th v-for="col in props.cols" :key="col.name" :props="props">
{{ col.label }}
@ -64,6 +65,19 @@
<q-tooltip>Card key credentials</q-tooltip>
</q-btn>
</q-td>
<q-td auto-width>
<q-btn
unelevated
dense
icon="query_stats"
:color="($q.dark.isActive) ? 'grey-7' : 'grey-5'"
type="a"
:href="'/boltcards/' + props.row.external_id"
target="_blank"
>
<q-tooltip>Card Stats</q-tooltip>
</q-btn>
</q-td>
<q-td v-for="col in props.cols" :key="col.name" :props="props">
{{ col.value }}
</q-td>

View file

@ -1,11 +1,15 @@
from http import HTTPStatus
from fastapi import Depends, Request
from fastapi.templating import Jinja2Templates
from starlette.exceptions import HTTPException
from starlette.responses import HTMLResponse
from lnbits.core.models import User
from lnbits.decorators import check_user_exists
from . import boltcards_ext, boltcards_renderer
from .crud import get_card_by_external_id, get_hits, get_refunds
templates = Jinja2Templates(directory="templates")
@ -15,3 +19,24 @@ async def index(request: Request, user: User = Depends(check_user_exists)):
return boltcards_renderer().TemplateResponse(
"boltcards/index.html", {"request": request, "user": user.dict()}
)
@boltcards_ext.get("/{card_id}", response_class=HTMLResponse)
async def display(request: Request, card_id: str):
card = await get_card_by_external_id(card_id)
if not card:
raise HTTPException(
status_code=HTTPStatus.NOT_FOUND, detail="Card does not exist."
)
hits = [hit.dict() for hit in await get_hits([card.id])]
refunds = [
refund.hit_id for refund in await get_refunds([hit["id"] for hit in hits])
]
card = card.dict()
# Remove wallet id from card dict
del card["wallet"]
return boltcards_renderer().TemplateResponse(
"boltcards/display.html",
{"request": request, "card": card, "hits": hits, "refunds": refunds},
)

View file

@ -23,7 +23,7 @@ from .models import Card, CreateCardData
@boltcards_ext.get("/api/v1/cards")
async def api_cards(
g: WalletTypeInfo = Depends(get_key_type), all_wallets: bool = Query(False)
g: WalletTypeInfo = Depends(get_key_type), all_wallets: bool = False
):
wallet_ids = [g.wallet.id]