Replaced events extension with almost working vuejs one
This commit is contained in:
parent
41277e6931
commit
ea3b858695
|
@ -1,8 +1,11 @@
|
||||||
<h1> LNEVENTS</h1>
|
<h1>Example Extension</h1>
|
||||||
<h2>Create/sell tickets for an event</h2>
|
<h2>*tagline*</h2>
|
||||||
<p>Events is an easy way to create/sell tickets for an event.
|
This is an example extension to help you organise and build you own.
|
||||||
|
|
||||||
It is advised to setup a specific wallet in lnbits for the event.</p>
|
Try to include an image
|
||||||
|
<img src="https://i.imgur.com/9i4xcQB.png">
|
||||||
|
|
||||||
<img src="https://i.imgur.com/qHi7ExL.png">
|
|
||||||
|
|
||||||
|
<h2>If your extension has API endpoints, include useful ones here</h2>
|
||||||
|
|
||||||
|
<code>curl -H "Content-type: application/json" -X POST https://YOUR-LNBITS/YOUR-EXTENSION/api/v1/EXAMPLE -d '{"amount":"100","memo":"example"}' -H "X-Api-Key: YOUR_WALLET-ADMIN/INVOICE-KEY"</code>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "Events",
|
"name": "Events",
|
||||||
"short_description": "LN tickets for events.",
|
"short_description": "Sell/register event tickets",
|
||||||
"icon": "local_activity",
|
"icon": "local_activity",
|
||||||
"contributors": ["arcbtc"]
|
"contributors": ["benarc"]
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +0,0 @@
|
||||||
{
|
|
||||||
"name": "Events",
|
|
||||||
"short_description": "LN tickets for events.",
|
|
||||||
"icon": "local_activity",
|
|
||||||
"contributors": ["arcbtc"]
|
|
||||||
}
|
|
116
lnbits/extensions/events/crud.py
Normal file
116
lnbits/extensions/events/crud.py
Normal file
|
@ -0,0 +1,116 @@
|
||||||
|
from typing import List, Optional, Union
|
||||||
|
|
||||||
|
from lnbits.db import open_ext_db
|
||||||
|
from lnbits.helpers import urlsafe_short_hash
|
||||||
|
|
||||||
|
from .models import Tickets, Events
|
||||||
|
|
||||||
|
|
||||||
|
#######TICKETS########
|
||||||
|
|
||||||
|
|
||||||
|
def create_ticket(wallet: str, event: str, name: str, email: str) -> Tickets:
|
||||||
|
with open_ext_db("events") as db:
|
||||||
|
eventdata = get_event(event)
|
||||||
|
sold = eventdata.sold + 1
|
||||||
|
amount_tickets = eventdata.amount_tickets - 1
|
||||||
|
ticket_id = urlsafe_short_hash()
|
||||||
|
db.execute(
|
||||||
|
"""
|
||||||
|
INSERT INTO tickets (id, wallet, event, name, email, registered)
|
||||||
|
VALUES (?, ?, ?, ?, ?, ?)
|
||||||
|
""",
|
||||||
|
(ticket_id, wallet, event, name, email, False),
|
||||||
|
)
|
||||||
|
db.execute(
|
||||||
|
"""
|
||||||
|
UPDATE events
|
||||||
|
SET sold = ?, amount_tickets = ?
|
||||||
|
WHERE id = ?
|
||||||
|
""",
|
||||||
|
(sold, amount_tickets, event),
|
||||||
|
)
|
||||||
|
return get_ticket(ticket_id)
|
||||||
|
|
||||||
|
|
||||||
|
def get_ticket(ticket_id: str) -> Optional[Tickets]:
|
||||||
|
with open_ext_db("events") as db:
|
||||||
|
row = db.fetchone("SELECT * FROM tickets WHERE id = ?", (ticket_id,))
|
||||||
|
|
||||||
|
return Tickets(**row) if row else None
|
||||||
|
|
||||||
|
|
||||||
|
def get_tickets(wallet_ids: Union[str, List[str]]) -> List[Tickets]:
|
||||||
|
if isinstance(wallet_ids, str):
|
||||||
|
wallet_ids = [wallet_ids]
|
||||||
|
|
||||||
|
with open_ext_db("events") as db:
|
||||||
|
q = ",".join(["?"] * len(wallet_ids))
|
||||||
|
rows = db.fetchall(f"SELECT * FROM tickets WHERE wallet IN ({q})", (*wallet_ids,))
|
||||||
|
print("scrum")
|
||||||
|
return [Tickets(**row) for row in rows]
|
||||||
|
|
||||||
|
|
||||||
|
def delete_ticket(ticket_id: str) -> None:
|
||||||
|
with open_ext_db("events") as db:
|
||||||
|
db.execute("DELETE FROM tickets WHERE id = ?", (ticket_id,))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
########EVENTS#########
|
||||||
|
|
||||||
|
|
||||||
|
def create_event(*, wallet: str, name: str, info: str, closing_date: str, event_start_date: str, event_end_date: str, amount_tickets: int, price_per_ticket: int) -> Events:
|
||||||
|
with open_ext_db("events") as db:
|
||||||
|
event_id = urlsafe_short_hash()
|
||||||
|
db.execute(
|
||||||
|
"""
|
||||||
|
INSERT INTO events (id, wallet, name, info, closing_date, event_start_date, event_end_date, amount_tickets, price_per_ticket, sold)
|
||||||
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||||
|
""",
|
||||||
|
(event_id, wallet, name, info, closing_date, event_start_date, event_end_date, amount_tickets, price_per_ticket, 0),
|
||||||
|
)
|
||||||
|
print(event_id)
|
||||||
|
|
||||||
|
return get_event(event_id)
|
||||||
|
|
||||||
|
def update_event(event_id: str, **kwargs) -> Events:
|
||||||
|
q = ", ".join([f"{field[0]} = ?" for field in kwargs.items()])
|
||||||
|
with open_ext_db("events") as db:
|
||||||
|
db.execute(f"UPDATE events SET {q} WHERE id = ?", (*kwargs.values(), event_id))
|
||||||
|
row = db.fetchone("SELECT * FROM events WHERE id = ?", (event_id,))
|
||||||
|
|
||||||
|
return Events(**row) if row else None
|
||||||
|
|
||||||
|
|
||||||
|
def get_event(event_id: str) -> Optional[Events]:
|
||||||
|
with open_ext_db("events") as db:
|
||||||
|
row = db.fetchone("SELECT * FROM events WHERE id = ?", (event_id,))
|
||||||
|
|
||||||
|
return Events(**row) if row else None
|
||||||
|
|
||||||
|
|
||||||
|
def get_events(wallet_ids: Union[str, List[str]]) -> List[Events]:
|
||||||
|
if isinstance(wallet_ids, str):
|
||||||
|
wallet_ids = [wallet_ids]
|
||||||
|
|
||||||
|
with open_ext_db("events") as db:
|
||||||
|
q = ",".join(["?"] * len(wallet_ids))
|
||||||
|
rows = db.fetchall(f"SELECT * FROM events WHERE wallet IN ({q})", (*wallet_ids,))
|
||||||
|
|
||||||
|
return [Events(**row) for row in rows]
|
||||||
|
|
||||||
|
|
||||||
|
def delete_event(event_id: str) -> None:
|
||||||
|
with open_ext_db("events") as db:
|
||||||
|
db.execute("DELETE FROM events WHERE id = ?", (event_id,))
|
||||||
|
|
||||||
|
########EVENTTICKETS#########
|
||||||
|
|
||||||
|
def get_event_tickets(event_id: str, wallet_id: str) -> Tickets:
|
||||||
|
|
||||||
|
with open_ext_db("events") as db:
|
||||||
|
rows = db.fetchall("SELECT * FROM tickets WHERE wallet = ? AND event = ?", (wallet_id, event_id))
|
||||||
|
print(rows)
|
||||||
|
|
||||||
|
return [Tickets(**row) for row in rows]
|
|
@ -1,5 +1,40 @@
|
||||||
from lnbits.db import open_ext_db
|
from lnbits.db import open_ext_db
|
||||||
|
|
||||||
|
def m001_initial(db):
|
||||||
|
|
||||||
|
db.execute(
|
||||||
|
"""
|
||||||
|
CREATE TABLE IF NOT EXISTS events (
|
||||||
|
id TEXT PRIMARY KEY,
|
||||||
|
wallet TEXT NOT NULL,
|
||||||
|
name TEXT NOT NULL,
|
||||||
|
info TEXT NOT NULL,
|
||||||
|
closing_date TEXT NOT NULL,
|
||||||
|
event_start_date TEXT NOT NULL,
|
||||||
|
event_end_date TEXT NOT NULL,
|
||||||
|
amount_tickets INTEGER NOT NULL,
|
||||||
|
price_per_ticket INTEGER NOT NULL,
|
||||||
|
sold INTEGER NOT NULL,
|
||||||
|
time TIMESTAMP NOT NULL DEFAULT (strftime('%s', 'now'))
|
||||||
|
);
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
db.execute(
|
||||||
|
"""
|
||||||
|
CREATE TABLE IF NOT EXISTS tickets (
|
||||||
|
id TEXT PRIMARY KEY,
|
||||||
|
wallet TEXT NOT NULL,
|
||||||
|
event TEXT NOT NULL,
|
||||||
|
name TEXT NOT NULL,
|
||||||
|
email TEXT NOT NULL,
|
||||||
|
registered BOOLEAN NOT NULL,
|
||||||
|
time TIMESTAMP NOT NULL DEFAULT (strftime('%s', 'now'))
|
||||||
|
);
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
def migrate():
|
def migrate():
|
||||||
print("pending")
|
with open_ext_db("events") as db:
|
||||||
|
m001_initial(db)
|
||||||
|
|
||||||
|
|
24
lnbits/extensions/events/models.py
Normal file
24
lnbits/extensions/events/models.py
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
from typing import NamedTuple
|
||||||
|
|
||||||
|
|
||||||
|
class Events(NamedTuple):
|
||||||
|
id: str
|
||||||
|
wallet: str
|
||||||
|
name: str
|
||||||
|
info: str
|
||||||
|
closing_date: str
|
||||||
|
event_start_date: str
|
||||||
|
event_end_date: str
|
||||||
|
amount_tickets: int
|
||||||
|
price_per_ticket: int
|
||||||
|
sold: int
|
||||||
|
time: int
|
||||||
|
|
||||||
|
class Tickets(NamedTuple):
|
||||||
|
id: str
|
||||||
|
wallet: str
|
||||||
|
event: str
|
||||||
|
name: str
|
||||||
|
email: str
|
||||||
|
registered: bool
|
||||||
|
time: int
|
|
@ -1,26 +0,0 @@
|
||||||
CREATE TABLE IF NOT EXISTS events (
|
|
||||||
key INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
||||||
usr TEXT,
|
|
||||||
wal TEXT,
|
|
||||||
walnme TEXT,
|
|
||||||
walinvkey INTEGER,
|
|
||||||
uni TEXT,
|
|
||||||
tit TEXT,
|
|
||||||
cldate TEXT,
|
|
||||||
notickets INTEGER,
|
|
||||||
sold INTEGER DEFAULT 0,
|
|
||||||
prtick INTEGER,
|
|
||||||
descr TEXT,
|
|
||||||
unireg TEXT
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS eventssold (
|
|
||||||
key INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
||||||
uni TEXT,
|
|
||||||
email TEXT,
|
|
||||||
name TEXT,
|
|
||||||
hash TEXT,
|
|
||||||
paid INTEGER DEFAULT 0,
|
|
||||||
reg INTEGER DEFAULT 0
|
|
||||||
);
|
|
||||||
|
|
27
lnbits/extensions/events/templates/events/_api_docs.html
Normal file
27
lnbits/extensions/events/templates/events/_api_docs.html
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
|
||||||
|
<q-expansion-item
|
||||||
|
group="extras"
|
||||||
|
icon="swap_vertical_circle"
|
||||||
|
label="Info"
|
||||||
|
:content-inset-level="0.5"
|
||||||
|
>
|
||||||
|
<q-card>
|
||||||
|
<q-card-section>
|
||||||
|
<h5 class="text-subtitle1 q-my-none">Events: Sell and register tickets for an event</h5>
|
||||||
|
<p>Events alows you to make a wave of tickets for an event. Once an attendee has paid for a ticket they get a unqiue code. Events comes with a shareable scanning frontend, so you can register the attendees<br/>
|
||||||
|
<small> Created by, <a href="https://github.com/benarc">Ben Arc</a></small></p>
|
||||||
|
</q-card>
|
||||||
|
</q-card-section>
|
||||||
|
|
||||||
|
</q-card-section>
|
||||||
|
</q-expansion-item>
|
||||||
|
<q-expansion-item
|
||||||
|
group="extras"
|
||||||
|
icon="swap_vertical_circle"
|
||||||
|
label="API info"
|
||||||
|
:content-inset-level="0.5"
|
||||||
|
>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</q-expansion-item>
|
|
@ -1,567 +1,202 @@
|
||||||
<!-- @format -->
|
{% extends "public.html" %} {% block page %}
|
||||||
|
<div class="row q-col-gutter-md justify-center">
|
||||||
<!DOCTYPE html>
|
<div class="col-12 col-md-7 col-lg-6 q-gutter-y-md">
|
||||||
<html>
|
<q-card class="q-pa-lg">
|
||||||
<head>
|
<q-card-section class="q-pa-none">
|
||||||
<meta charset="UTF-8" />
|
<h3 class="q-my-none">{{ event_name }}</h3>
|
||||||
<title>LNBits Wallet</title>
|
|
||||||
<meta
|
|
||||||
content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"
|
|
||||||
name="viewport"
|
|
||||||
/>
|
|
||||||
<!-- Bootstrap 3.3.2 -->
|
|
||||||
<link
|
|
||||||
rel="stylesheet"
|
|
||||||
media="screen"
|
|
||||||
href="{{ url_for('static', filename='bootstrap/css/bootstrap.min.css') }}"
|
|
||||||
/>
|
|
||||||
<!-- FontAwesome 4.3.0 -->
|
|
||||||
<link
|
|
||||||
href="https://maxcdn.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.min.css"
|
|
||||||
rel="stylesheet"
|
|
||||||
type="text/css"
|
|
||||||
/>
|
|
||||||
<!-- Ionicons 2.0.0 -->
|
|
||||||
<link
|
|
||||||
href="https://code.ionicframework.com/ionicons/2.0.0/css/ionicons.min.css"
|
|
||||||
rel="stylesheet"
|
|
||||||
type="text/css"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<!-- Theme style -->
|
|
||||||
<link
|
|
||||||
rel="stylesheet"
|
|
||||||
media="screen"
|
|
||||||
href="{{ url_for('static', filename='dist/css/AdminLTE.min.css') }}"
|
|
||||||
/>
|
|
||||||
<!-- AdminLTE Skins. Choose a skin from the css/skins
|
|
||||||
folder instead of downloading all of them to reduce the load. -->
|
|
||||||
|
|
||||||
<link
|
|
||||||
rel="stylesheet"
|
|
||||||
media="screen"
|
|
||||||
href="{{ url_for('static', filename='dist/css/skins/_all-skins.min.css') }}"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<!-- Morris chart -->
|
|
||||||
<link
|
|
||||||
rel="stylesheet"
|
|
||||||
media="screen"
|
|
||||||
href="{{ url_for('static', filename='plugins/morris/morris.css') }}"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<!-- jvectormap -->
|
|
||||||
<link
|
|
||||||
rel="stylesheet"
|
|
||||||
media="screen"
|
|
||||||
href="{{ url_for('static', filename='plugins/jvectormap/jquery-jvectormap-1.2.2.css') }}"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<!-- bootstrap wysihtml5 - text editor -->
|
|
||||||
<link
|
|
||||||
rel="stylesheet"
|
|
||||||
media="screen"
|
|
||||||
href="{{ url_for('static', filename='plugins/bootstrap-wysihtml5/bootstrap3-wysihtml5.min.css') }}"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->
|
|
||||||
<!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
|
|
||||||
<!--[if lt IE 9]>
|
|
||||||
<script src="https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
|
|
||||||
<script src="https://oss.maxcdn.com/libs/respond.js/1.3.0/respond.min.js"></script>
|
|
||||||
<![endif]-->
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.small-box > .small-box-footer {
|
|
||||||
text-align: left;
|
|
||||||
padding-left: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#loadingMessage {
|
|
||||||
text-align: center;
|
|
||||||
padding: 40px;
|
|
||||||
background-color: #eee;
|
|
||||||
}
|
|
||||||
|
|
||||||
#canvas {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
#output {
|
|
||||||
margin-top: 20px;
|
|
||||||
background: #eee;
|
|
||||||
padding: 10px;
|
|
||||||
padding-bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#output div {
|
|
||||||
padding-bottom: 10px;
|
|
||||||
word-wrap: break-word;
|
|
||||||
}
|
|
||||||
|
|
||||||
#noQRFound {
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<!-- jQuery 2.1.3 -->
|
|
||||||
<script src="{{ url_for('static', filename='plugins/jQuery/jQuery-2.1.3.min.js') }}"></script>
|
|
||||||
<!-- jQuery UI 1.11.2 -->
|
|
||||||
<script
|
|
||||||
src="https://code.jquery.com/ui/1.11.2/jquery-ui.min.js"
|
|
||||||
type="text/javascript"
|
|
||||||
></script>
|
|
||||||
<!-- Resolve conflict in jQuery UI tooltip with Bootstrap tooltip -->
|
|
||||||
<script>
|
|
||||||
$.widget.bridge('uibutton', $.ui.button)
|
|
||||||
</script>
|
|
||||||
<!-- Bootstrap 3.3.2 JS -->
|
|
||||||
<script
|
|
||||||
src="{{ url_for('static', filename='bootstrap/js/bootstrap.min.js') }}"
|
|
||||||
type="text/javascript"
|
|
||||||
></script>
|
|
||||||
<!-- Morris.js charts -->
|
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/raphael/2.1.0/raphael-min.js"></script>
|
|
||||||
<script
|
|
||||||
src="{{ url_for('static', filename='plugins/morris/morris.min.js') }}"
|
|
||||||
type="text/javascript"
|
|
||||||
></script>
|
|
||||||
<!-- Sparkline -->
|
|
||||||
<script
|
|
||||||
src="{{ url_for('static', filename='plugins/sparkline/jquery.sparkline.min.js') }}"
|
|
||||||
type="text/javascript"
|
|
||||||
></script>
|
|
||||||
<!-- jvectormap -->
|
|
||||||
<script
|
|
||||||
src="{{ url_for('static', filename='plugins/jvectormap/jquery-jvectormap-1.2.2.min.js') }}"
|
|
||||||
type="text/javascript"
|
|
||||||
></script>
|
|
||||||
<script
|
|
||||||
src="{{ url_for('static', filename='plugins/jvectormap/jquery-jvectormap-world-mill-en.js') }}"
|
|
||||||
type="text/javascript"
|
|
||||||
></script>
|
|
||||||
<!-- jQuery Knob Chart -->
|
|
||||||
<script
|
|
||||||
src="{{ url_for('static', filename='plugins/knob/jquery.knob.js') }}"
|
|
||||||
type="text/javascript"
|
|
||||||
></script>
|
|
||||||
<!-- Bootstrap WYSIHTML5 -->
|
|
||||||
<script
|
|
||||||
src="{{ url_for('static', filename='plugins/bootstrap-wysihtml5/bootstrap3-wysihtml5.all.min.js') }}"
|
|
||||||
type="text/javascript"
|
|
||||||
></script>
|
|
||||||
<!-- Slimscroll -->
|
|
||||||
<script
|
|
||||||
src="{{ url_for('static', filename='plugins/slimScroll/jquery.slimscroll.min.js') }}"
|
|
||||||
type="text/javascript"
|
|
||||||
></script>
|
|
||||||
<!-- FastClick -->
|
|
||||||
<script src="{{ url_for('static', filename='plugins/fastclick/fastclick.min.js') }}"></script>
|
|
||||||
<!-- AdminLTE App -->
|
|
||||||
<script
|
|
||||||
src="{{ url_for('static', filename='dist/js/app.min.js') }}"
|
|
||||||
type="text/javascript"
|
|
||||||
></script>
|
|
||||||
|
|
||||||
<!-- AdminLTE dashboard demo (This is only for demo purposes) -->
|
|
||||||
<script
|
|
||||||
src="{{ url_for('static', filename='dist/js/pages/dashboard.js') }}"
|
|
||||||
type="text/javascript"
|
|
||||||
></script>
|
|
||||||
|
|
||||||
<!-- AdminLTE for demo purposes -->
|
|
||||||
<script
|
|
||||||
src="{{ url_for('static', filename='dist/js/demo.js') }}"
|
|
||||||
type="text/javascript"
|
|
||||||
></script>
|
|
||||||
|
|
||||||
<script
|
|
||||||
src="{{ url_for('static', filename='plugins/datatables/jquery.dataTables.js') }}"
|
|
||||||
type="text/javascript"
|
|
||||||
></script>
|
|
||||||
<link
|
|
||||||
rel="stylesheet"
|
|
||||||
href="//cdnjs.cloudflare.com/ajax/libs/morris.js/0.5.1/morris.css"
|
|
||||||
/>
|
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/raphael/2.1.0/raphael-min.js"></script>
|
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/morris.js/0.5.1/morris.min.js"></script>
|
|
||||||
<script
|
|
||||||
src="{{ url_for('static', filename='plugins/jscam/JS.js') }}"
|
|
||||||
type="text/javascript"
|
|
||||||
></script>
|
|
||||||
<script
|
|
||||||
src="{{ url_for('static', filename='plugins/jscam/qrcode.min.js') }}"
|
|
||||||
type="text/javascript"
|
|
||||||
></script>
|
|
||||||
<script
|
|
||||||
src="{{ url_for('static', filename='plugins/bolt11/decoder.js') }}"
|
|
||||||
type="text/javascript"
|
|
||||||
></script>
|
|
||||||
<script
|
|
||||||
src="{{ url_for('static', filename='plugins/bolt11/utils.js') }}"
|
|
||||||
type="text/javascript"
|
|
||||||
></script>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
//GOOFY CSS HACK TO GO DARK
|
|
||||||
|
|
||||||
.skin-blue .wrapper {
|
|
||||||
background: #1f2234;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
color: #fff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.skin-blue .sidebar-menu > li.active > a {
|
|
||||||
color: #fff;
|
|
||||||
background: #1f2234;
|
|
||||||
border-left-color: #8964a9;
|
|
||||||
}
|
|
||||||
|
|
||||||
.skin-blue .main-header .navbar {
|
|
||||||
background-color: #2e507d;
|
|
||||||
}
|
|
||||||
|
|
||||||
.content-wrapper,
|
|
||||||
.right-side {
|
|
||||||
background-color: #1f2234;
|
|
||||||
}
|
|
||||||
.skin-blue .main-header .logo {
|
|
||||||
background-color: #1f2234;
|
|
||||||
color: #fff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.skin-blue .sidebar-menu > li.header {
|
|
||||||
color: #4b646f;
|
|
||||||
background: #1f2234;
|
|
||||||
}
|
|
||||||
.skin-blue .wrapper,
|
|
||||||
.skin-blue .main-sidebar,
|
|
||||||
.skin-blue .left-side {
|
|
||||||
background: #1f2234;
|
|
||||||
}
|
|
||||||
|
|
||||||
.skin-blue .sidebar-menu > li > .treeview-menu {
|
|
||||||
margin: 0 1px;
|
|
||||||
background: #1f2234;
|
|
||||||
}
|
|
||||||
|
|
||||||
.skin-blue .sidebar-menu > li > a {
|
|
||||||
border-left: 3px solid transparent;
|
|
||||||
margin-right: 1px;
|
|
||||||
}
|
|
||||||
.skin-blue .sidebar-menu > li > a:hover,
|
|
||||||
.skin-blue .sidebar-menu > li.active > a {
|
|
||||||
color: #fff;
|
|
||||||
background: #3e355a;
|
|
||||||
border-left-color: #8964a9;
|
|
||||||
}
|
|
||||||
|
|
||||||
.skin-blue .main-header .logo:hover {
|
|
||||||
background: #3e355a;
|
|
||||||
}
|
|
||||||
|
|
||||||
.skin-blue .main-header .navbar .sidebar-toggle:hover {
|
|
||||||
background-color: #3e355a;
|
|
||||||
}
|
|
||||||
.main-footer {
|
|
||||||
background-color: #1f2234;
|
|
||||||
padding: 15px;
|
|
||||||
color: #fff;
|
|
||||||
border-top: 0px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.skin-blue .main-header .navbar {
|
|
||||||
background-color: #1f2234;
|
|
||||||
}
|
|
||||||
|
|
||||||
.bg-red,
|
|
||||||
.callout.callout-danger,
|
|
||||||
.alert-danger,
|
|
||||||
.alert-error,
|
|
||||||
.label-danger,
|
|
||||||
.modal-danger .modal-body {
|
|
||||||
background-color: #1f2234 !important;
|
|
||||||
}
|
|
||||||
.alert-danger,
|
|
||||||
.alert-error {
|
|
||||||
border-color: #fff;
|
|
||||||
border: 1px solid #fff;
|
|
||||||
border-radius: 7px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.skin-blue .main-header .navbar .nav > li > a:hover,
|
|
||||||
.skin-blue .main-header .navbar .nav > li > a:active,
|
|
||||||
.skin-blue .main-header .navbar .nav > li > a:focus,
|
|
||||||
.skin-blue .main-header .navbar .nav .open > a,
|
|
||||||
.skin-blue .main-header .navbar .nav .open > a:hover,
|
|
||||||
.skin-blue .main-header .navbar .nav .open > a:focus {
|
|
||||||
color: #f6f6f6;
|
|
||||||
background-color: #3e355a;
|
|
||||||
}
|
|
||||||
.bg-aqua,
|
|
||||||
.callout.callout-info,
|
|
||||||
.alert-info,
|
|
||||||
.label-info,
|
|
||||||
.modal-info .modal-body {
|
|
||||||
background-color: #3e355a !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.box {
|
|
||||||
position: relative;
|
|
||||||
border-radius: 3px;
|
|
||||||
background-color: #333646;
|
|
||||||
border-top: 3px solid #8964a9;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
.table-striped > tbody > tr:nth-of-type(2n + 1) {
|
|
||||||
background-color: #333646;
|
|
||||||
}
|
|
||||||
|
|
||||||
.box-header {
|
|
||||||
color: #fff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.box.box-danger {
|
|
||||||
border-top-color: #8964a9;
|
|
||||||
}
|
|
||||||
.box.box-primary {
|
|
||||||
border-top-color: #8964a9;
|
|
||||||
}
|
|
||||||
|
|
||||||
a {
|
|
||||||
color: #8964a9;
|
|
||||||
}
|
|
||||||
.box-header.with-border {
|
|
||||||
border-bottom: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
a:hover,
|
|
||||||
a:active,
|
|
||||||
a:focus {
|
|
||||||
outline: none;
|
|
||||||
text-decoration: none;
|
|
||||||
color: #fff;
|
|
||||||
}
|
|
||||||
// .modal.in .modal-dialog{
|
|
||||||
// color:#000;
|
|
||||||
// }
|
|
||||||
|
|
||||||
.form-control {
|
|
||||||
background-color: #333646;
|
|
||||||
color: #fff;
|
|
||||||
}
|
|
||||||
.box-footer {
|
|
||||||
border-top: none;
|
|
||||||
|
|
||||||
background-color: #333646;
|
|
||||||
}
|
|
||||||
.modal-footer {
|
|
||||||
border-top: none;
|
|
||||||
}
|
|
||||||
.modal-content {
|
|
||||||
background-color: #333646;
|
|
||||||
}
|
|
||||||
.modal.in .modal-dialog {
|
|
||||||
background-color: #333646;
|
|
||||||
}
|
|
||||||
|
|
||||||
.layout-boxed {
|
|
||||||
background: none;
|
|
||||||
background-color: rgba(0, 0, 0, 0);
|
|
||||||
background-color: #3e355a;
|
|
||||||
}
|
|
||||||
|
|
||||||
.skin-blue .sidebar-menu > li > a:hover,
|
|
||||||
.skin-blue .sidebar-menu > li.active > a {
|
|
||||||
background: none;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body class="skin-blue layout-boxed sidebar-collapse sidebar-open">
|
|
||||||
<div class="wrapper">
|
|
||||||
<header class="main-header">
|
|
||||||
<!-- Logo -->
|
|
||||||
<a href="{{ url_for('core.home') }}" class="logo"><b>LN</b>bits</a>
|
|
||||||
<!-- Header Navbar: style can be found in header.less -->
|
|
||||||
<nav class="navbar navbar-static-top" role="navigation">
|
|
||||||
<!-- Sidebar toggle button-->
|
|
||||||
<a
|
|
||||||
href="#"
|
|
||||||
class="sidebar-toggle"
|
|
||||||
data-toggle="offcanvas"
|
|
||||||
role="button"
|
|
||||||
>
|
|
||||||
<span class="sr-only">Toggle navigation</span>
|
|
||||||
</a>
|
|
||||||
<div class="navbar-custom-menu">
|
|
||||||
<ul class="nav navbar-nav">
|
|
||||||
<!-- Messages: style can be found in dropdown.less-->
|
|
||||||
<li class="dropdown messages-menu">
|
|
||||||
{% block messages %}{% endblock %}
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</nav>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
<aside class="main-sidebar">
|
|
||||||
<!-- sidebar: style can be found in sidebar.less -->
|
|
||||||
<section class="sidebar" style="height: auto;"></section>
|
|
||||||
<!-- /.sidebar -->
|
|
||||||
</aside>
|
|
||||||
|
|
||||||
<!-- Right side column. Contains the navbar and content of the page -->
|
|
||||||
<div class="content-wrapper">
|
|
||||||
<!-- Content Header (Page header) -->
|
|
||||||
<section class="content-header">
|
|
||||||
<h1>
|
|
||||||
LNBits Events
|
|
||||||
<small>Lightning powered tickets</small>
|
|
||||||
</h1>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<!-- Main content -->
|
|
||||||
<section class="content">
|
|
||||||
<br /><br />
|
|
||||||
<center><h1 style="font-size: 500%;">{{ nme }}</h1></center>
|
|
||||||
<center>
|
|
||||||
<h2 style="width: 55%; word-wrap: break-word;">{{ descr }}</h2>
|
|
||||||
</center>
|
|
||||||
<div id="theform">
|
|
||||||
<br /><br /><br />
|
|
||||||
<center>
|
|
||||||
<form role="form">
|
|
||||||
<div class="form-group" style="width: 300px;">
|
|
||||||
<input
|
|
||||||
id="Nam"
|
|
||||||
type="text"
|
|
||||||
class="form-control"
|
|
||||||
placeholder="Name"
|
|
||||||
/>
|
|
||||||
<input
|
|
||||||
id="Ema"
|
|
||||||
type="text"
|
|
||||||
class="form-control"
|
|
||||||
placeholder="Email"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<button
|
|
||||||
onclick="submitforticket()"
|
|
||||||
type="button"
|
|
||||||
class="btn btn-info"
|
|
||||||
>
|
|
||||||
Go to payment
|
|
||||||
</button>
|
|
||||||
<p style="color: red;" id="error"></p>
|
|
||||||
</form>
|
|
||||||
</center>
|
|
||||||
</div>
|
|
||||||
<center>
|
|
||||||
<br /><br />
|
|
||||||
<div id="qrcode" style="width: 340px;"></div>
|
|
||||||
<br /><br />
|
|
||||||
<div
|
|
||||||
style="width: 55%; word-wrap: break-word;"
|
|
||||||
id="qrcodetxt"
|
|
||||||
></div>
|
|
||||||
<br />
|
<br />
|
||||||
</center>
|
<h5 class="q-my-none">{{ event_info }}</h5>
|
||||||
</section>
|
<br />
|
||||||
<!-- /.content -->
|
<q-form @submit="Invoice()" class="q-gutter-md">
|
||||||
|
<q-input
|
||||||
|
filled
|
||||||
|
dense
|
||||||
|
v-model.trim="formDialog.data.name"
|
||||||
|
type="name"
|
||||||
|
label="Your name "
|
||||||
|
></q-input>
|
||||||
|
<q-input
|
||||||
|
filled
|
||||||
|
dense
|
||||||
|
v-model.trim="formDialog.data.email"
|
||||||
|
type="email"
|
||||||
|
label="Your email "
|
||||||
|
></q-input>
|
||||||
|
|
||||||
|
<p>{% raw %}{{amountWords}}{% endraw %}</p>
|
||||||
|
<div class="row q-mt-lg">
|
||||||
|
<q-btn
|
||||||
|
unelevated
|
||||||
|
color="deep-purple"
|
||||||
|
:disable="formDialog.data.name == '' || formDialog.data.email == '' || paymentReq"
|
||||||
|
type="submit"
|
||||||
|
>Submit</q-btn
|
||||||
|
>
|
||||||
|
<q-btn @click="resetForm" flat color="grey" class="q-ml-auto"
|
||||||
|
>Cancel</q-btn
|
||||||
|
>
|
||||||
</div>
|
</div>
|
||||||
<!-- /.content-wrapper -->
|
</q-form>
|
||||||
|
</q-card-section>
|
||||||
|
</q-card>
|
||||||
|
|
||||||
|
<q-card v-show="ticketLink.show" class="q-pa-lg">
|
||||||
|
<div class="text-center q-mb-lg">
|
||||||
|
<a :href="ticketLink.data.link" target="_blank">
|
||||||
|
<q-btn unelevated size="xl" color="deep-purple"
|
||||||
|
>Link to your ticket!</q-btn
|
||||||
|
></a
|
||||||
|
>
|
||||||
|
<br /><br />
|
||||||
|
<p>You'll be redirected in 5 seconds...</p>
|
||||||
|
</div>
|
||||||
|
</q-card>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
|
||||||
|
|
||||||
<script>
|
<q-dialog v-model="receive.show" position="top" @hide="closeReceiveDialog">
|
||||||
function postAjax(url, data, thekey, success) {
|
<q-card
|
||||||
var params =
|
v-if="!receive.paymentReq"
|
||||||
typeof data == 'string'
|
class="q-pa-lg q-pt-xl lnbits__dialog-card"
|
||||||
? data
|
>
|
||||||
: Object.keys(data)
|
</q-card>
|
||||||
.map(function (k) {
|
<q-card v-else class="q-pa-lg q-pt-xl lnbits__dialog-card">
|
||||||
return encodeURIComponent(k) + '=' + encodeURIComponent(data[k])
|
<div class="text-center q-mb-lg">
|
||||||
|
<a :href="'lightning:' + receive.paymentReq">
|
||||||
|
<q-responsive :ratio="1" class="q-mx-xl">
|
||||||
|
<qrcode
|
||||||
|
:value="paymentReq"
|
||||||
|
:options="{width: 340}"
|
||||||
|
class="rounded-borders"
|
||||||
|
></qrcode>
|
||||||
|
</q-responsive>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="row q-mt-lg">
|
||||||
|
<q-btn outline color="grey" @click="copyText(receive.paymentReq)"
|
||||||
|
>Copy invoice</q-btn
|
||||||
|
>
|
||||||
|
<q-btn v-close-popup flat color="grey" class="q-ml-auto">Close</q-btn>
|
||||||
|
</div>
|
||||||
|
</q-card>
|
||||||
|
</q-dialog>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% endblock %} {% block scripts %}
|
||||||
|
<script src="{{ url_for('static', filename='vendor/vue-qrcode@1.0.2/vue-qrcode.min.js') }}"></script>
|
||||||
|
<script>
|
||||||
|
console.log('{{ form_costpword }}')
|
||||||
|
Vue.component(VueQrcode.name, VueQrcode)
|
||||||
|
|
||||||
|
new Vue({
|
||||||
|
el: '#vue',
|
||||||
|
mixins: [windowMixin],
|
||||||
|
data: function () {
|
||||||
|
return {
|
||||||
|
paymentReq: null,
|
||||||
|
redirectUrl: null,
|
||||||
|
formDialog: {
|
||||||
|
show: false,
|
||||||
|
data: {
|
||||||
|
name: '',
|
||||||
|
email: ''
|
||||||
|
}
|
||||||
|
},
|
||||||
|
ticketLink: {
|
||||||
|
show: false,
|
||||||
|
data: {
|
||||||
|
link: ''
|
||||||
|
}
|
||||||
|
},
|
||||||
|
receive: {
|
||||||
|
show: false,
|
||||||
|
status: 'pending',
|
||||||
|
paymentReq: null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
resetForm: function (e) {
|
||||||
|
e.preventDefault()
|
||||||
|
this.formDialog.data.name = ''
|
||||||
|
this.formDialog.data.email = ''
|
||||||
|
},
|
||||||
|
|
||||||
|
closeReceiveDialog: function () {
|
||||||
|
var checker = this.receive.paymentChecker
|
||||||
|
dismissMsg()
|
||||||
|
|
||||||
|
clearInterval(paymentChecker)
|
||||||
|
setTimeout(function () {}, 10000)
|
||||||
|
},
|
||||||
|
Invoice: function () {
|
||||||
|
var self = this
|
||||||
|
axios
|
||||||
|
|
||||||
|
.get('/events/api/v1/tickets/' + '{{ event_id }}/{{ event_price }}')
|
||||||
|
|
||||||
|
.then(function (response) {
|
||||||
|
self.paymentReq = response.data.payment_request
|
||||||
|
self.paymentCheck = response.data.checking_id
|
||||||
|
|
||||||
|
dismissMsg = self.$q.notify({
|
||||||
|
timeout: 0,
|
||||||
|
message: 'Waiting for payment...'
|
||||||
})
|
})
|
||||||
.join('&')
|
|
||||||
var xhr = window.XMLHttpRequest
|
self.receive = {
|
||||||
? new XMLHttpRequest()
|
show: true,
|
||||||
: new ActiveXObject('Microsoft.XMLHTTP')
|
status: 'pending',
|
||||||
xhr.open('POST', url)
|
paymentReq: self.paymentReq
|
||||||
xhr.onreadystatechange = function () {
|
|
||||||
if (xhr.readyState > 3 && xhr.status == 200) {
|
|
||||||
success(xhr.responseText)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
xhr.setRequestHeader('X-Api-Key', thekey)
|
|
||||||
xhr.setRequestHeader('Content-Type', 'application/json')
|
|
||||||
xhr.send(params)
|
|
||||||
return xhr
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function getAjax(url, thekey, success) {
|
paymentChecker = setInterval(function () {
|
||||||
var xhr = window.XMLHttpRequest
|
axios
|
||||||
? new XMLHttpRequest()
|
.post('/events/api/v1/tickets/' + self.paymentCheck, {
|
||||||
: new ActiveXObject('Microsoft.XMLHTTP')
|
event: '{{ event_id }}',
|
||||||
xhr.open('GET', url, true)
|
name: self.formDialog.data.name,
|
||||||
xhr.onreadystatechange = function () {
|
email: self.formDialog.data.email
|
||||||
if (xhr.readyState > 3 && xhr.status == 200) {
|
|
||||||
success(xhr.responseText)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
xhr.setRequestHeader('X-Api-Key', thekey)
|
|
||||||
xhr.setRequestHeader('Content-Type', 'application/json')
|
|
||||||
|
|
||||||
xhr.send()
|
|
||||||
return xhr
|
|
||||||
}
|
|
||||||
|
|
||||||
function submitforticket() {
|
|
||||||
nam = document.getElementById('Nam').value
|
|
||||||
ema = document.getElementById('Ema').value
|
|
||||||
|
|
||||||
postAjax(
|
|
||||||
"{{ url_for('events.api_getticket') }}?ema=" + ema,
|
|
||||||
JSON.stringify({unireg: '{{wave }}', name: nam}),
|
|
||||||
'filla',
|
|
||||||
|
|
||||||
function (data) {
|
|
||||||
theinvoice = JSON.parse(data).pay_req
|
|
||||||
thehash = JSON.parse(data).payment_hash
|
|
||||||
|
|
||||||
new QRCode(document.getElementById('qrcode'), {
|
|
||||||
text: theinvoice,
|
|
||||||
width: 300,
|
|
||||||
height: 300,
|
|
||||||
colorDark: '#000000',
|
|
||||||
colorLight: '#ffffff',
|
|
||||||
correctLevel: QRCode.CorrectLevel.M
|
|
||||||
})
|
})
|
||||||
document.getElementById('theform').innerHTML = ''
|
.then(function (res) {
|
||||||
|
if (res.data.paid) {
|
||||||
|
clearInterval(paymentChecker)
|
||||||
|
dismissMsg()
|
||||||
|
self.formDialog.data.name = ''
|
||||||
|
self.formDialog.data.email = ''
|
||||||
|
|
||||||
document.getElementById('qrcode').style.backgroundColor = 'white'
|
self.$q.notify({
|
||||||
document.getElementById('qrcode').style.padding = '20px'
|
type: 'positive',
|
||||||
|
message: 'Sent, thank you!',
|
||||||
|
icon: null
|
||||||
|
})
|
||||||
|
self.receive = {
|
||||||
|
show: false,
|
||||||
|
status: 'complete',
|
||||||
|
paymentReq: null
|
||||||
|
}
|
||||||
|
|
||||||
document.getElementById('qrcodetxt').innerHTML =
|
self.ticketLink = {
|
||||||
theinvoice + '<br/><br/>'
|
show: true,
|
||||||
|
data: {
|
||||||
var refreshId = setInterval(function () {
|
link: '/events/ticket/' + res.data.ticket_id
|
||||||
getAjax('/api/v1/invoice/' + thehash, '{{wave}}', function (datab) {
|
}
|
||||||
console.log(JSON.parse(datab).PAID)
|
}
|
||||||
if (JSON.parse(datab).PAID == 'TRUE') {
|
setTimeout(function () {
|
||||||
location.replace(
|
window.location.href =
|
||||||
"{{ url_for('events.ticket') }}?hash=" +
|
'/events/ticket/' + res.data.ticket_id
|
||||||
thehash +
|
}, 5000)
|
||||||
'&unireg={{wave}}'
|
|
||||||
)
|
|
||||||
clearInterval(refreshId)
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}, 3000)
|
.catch(function (error) {
|
||||||
|
LNbits.utils.notifyApiError(error)
|
||||||
|
})
|
||||||
|
}, 2000)
|
||||||
|
})
|
||||||
|
.catch(function (error) {
|
||||||
|
LNbits.utils.notifyApiError(error)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
)
|
|
||||||
}
|
}
|
||||||
</script>
|
})
|
||||||
</html>
|
</script>
|
||||||
|
{% endblock %}
|
||||||
|
|
35
lnbits/extensions/events/templates/events/error.html
Normal file
35
lnbits/extensions/events/templates/events/error.html
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
{% extends "public.html" %} {% block page %}
|
||||||
|
<div class="row q-col-gutter-md justify-center">
|
||||||
|
<div class="col-12 col-md-7 col-lg-6 q-gutter-y-md">
|
||||||
|
<q-card class="q-pa-lg">
|
||||||
|
<q-card-section class="q-pa-none">
|
||||||
|
<center>
|
||||||
|
<h3 class="q-my-none">{{ event_name }} error</h3>
|
||||||
|
<br />
|
||||||
|
<q-icon
|
||||||
|
name="warning"
|
||||||
|
class="text-grey"
|
||||||
|
style="font-size: 20rem;"
|
||||||
|
></q-icon>
|
||||||
|
|
||||||
|
<h5 class="q-my-none">{{ event_error }}</h5>
|
||||||
|
<br />
|
||||||
|
</center>
|
||||||
|
</q-card-section>
|
||||||
|
</q-card>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% endblock %} {% block scripts %}
|
||||||
|
|
||||||
|
<script>
|
||||||
|
new Vue({
|
||||||
|
el: '#vue',
|
||||||
|
mixins: [windowMixin],
|
||||||
|
data: function () {
|
||||||
|
return {}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
|
</div>
|
File diff suppressed because it is too large
Load Diff
146
lnbits/extensions/events/templates/events/register.html
Normal file
146
lnbits/extensions/events/templates/events/register.html
Normal file
|
@ -0,0 +1,146 @@
|
||||||
|
{% extends "public.html" %} {% block page %}
|
||||||
|
<div class="row q-col-gutter-md justify-center">
|
||||||
|
<div class="col-12 col-md-7 col-lg-6 q-gutter-y-md">
|
||||||
|
<q-card class="q-pa-lg">
|
||||||
|
<q-card-section class="q-pa-none">
|
||||||
|
<center>
|
||||||
|
<h3 class="q-my-none">{{ event_name }} Registration</h3>
|
||||||
|
<br />
|
||||||
|
|
||||||
|
<br />
|
||||||
|
|
||||||
|
<q-btn unelevated color="deep-purple" @click="showCamera" size="xl"
|
||||||
|
>Scan ticket</q-btn
|
||||||
|
>
|
||||||
|
</center>
|
||||||
|
</q-card-section>
|
||||||
|
</q-card>
|
||||||
|
|
||||||
|
<q-card>
|
||||||
|
<q-card-section>
|
||||||
|
<q-table
|
||||||
|
dense
|
||||||
|
flat
|
||||||
|
:data="tickets"
|
||||||
|
row-key="id"
|
||||||
|
:columns="ticketsTable.columns"
|
||||||
|
:pagination.sync="ticketsTable.pagination"
|
||||||
|
>
|
||||||
|
{% raw %}
|
||||||
|
<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">
|
||||||
|
{{ col.label }}
|
||||||
|
</q-th>
|
||||||
|
</q-tr>
|
||||||
|
</template>
|
||||||
|
<template v-slot:body="props">
|
||||||
|
<q-tr :props="props">
|
||||||
|
<q-td auto-width>
|
||||||
|
<q-btn
|
||||||
|
unelevated
|
||||||
|
dense
|
||||||
|
size="xs"
|
||||||
|
icon="local_activity"
|
||||||
|
:color="($q.dark.isActive) ? 'grey-7' : 'grey-5'"
|
||||||
|
type="a"
|
||||||
|
:href="'/events/ticket/' + props.row.id"
|
||||||
|
target="_blank"
|
||||||
|
></q-btn>
|
||||||
|
</q-td>
|
||||||
|
|
||||||
|
<q-td v-for="col in props.cols" :key="col.name" :props="props">
|
||||||
|
{{ col.value }}
|
||||||
|
</q-td>
|
||||||
|
</q-tr>
|
||||||
|
</template>
|
||||||
|
{% endraw %}
|
||||||
|
</q-table>
|
||||||
|
</q-card-section>
|
||||||
|
</q-card>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %} {% block styles %}
|
||||||
|
<link
|
||||||
|
rel="stylesheet"
|
||||||
|
type="text/css"
|
||||||
|
href="{{ url_for('static', filename='vendor/vue-qrcode-reader@2.2.0/vue-qrcode-reader.min.css') }}"
|
||||||
|
/>
|
||||||
|
{% endblock %} {% block scripts %}
|
||||||
|
<script src="{{ url_for('static', filename='vendor/vue-qrcode@1.0.2/vue-qrcode.min.js') }}"></script>
|
||||||
|
<script>
|
||||||
|
Vue.component(VueQrcode.name, VueQrcode)
|
||||||
|
|
||||||
|
var mapEvents = function (obj) {
|
||||||
|
obj.date = Quasar.utils.date.formatDate(
|
||||||
|
new Date(obj.time * 1000),
|
||||||
|
'YYYY-MM-DD HH:mm'
|
||||||
|
)
|
||||||
|
obj.fsat = new Intl.NumberFormat(LOCALE).format(obj.amount)
|
||||||
|
obj.displayUrl = ['/events/', obj.id].join('')
|
||||||
|
return obj
|
||||||
|
}
|
||||||
|
new Vue({
|
||||||
|
el: '#vue',
|
||||||
|
mixins: [windowMixin],
|
||||||
|
data: function () {
|
||||||
|
return {
|
||||||
|
tickets: [],
|
||||||
|
ticketsTable: {
|
||||||
|
columns: [
|
||||||
|
{name: 'id', align: 'left', label: 'ID', field: 'id'},
|
||||||
|
{name: 'name', align: 'left', label: 'Name', field: 'name'},
|
||||||
|
{
|
||||||
|
name: 'registered',
|
||||||
|
align: 'left',
|
||||||
|
label: 'Registered',
|
||||||
|
field: 'registered'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
pagination: {
|
||||||
|
rowsPerPage: 10
|
||||||
|
}
|
||||||
|
},
|
||||||
|
sendCamera: {
|
||||||
|
show: true,
|
||||||
|
camera: 'auto'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
closeCamera: function () {
|
||||||
|
this.sendCamera.show = false
|
||||||
|
},
|
||||||
|
showCamera: function () {
|
||||||
|
this.sendCamera.show = true
|
||||||
|
},
|
||||||
|
getEventTickets: function () {
|
||||||
|
var self = this
|
||||||
|
console.log('obj')
|
||||||
|
LNbits.api
|
||||||
|
.request(
|
||||||
|
'GET',
|
||||||
|
'/events/api/v1/eventtickets/{{ wallet_id }}/{{ event_id }}'
|
||||||
|
)
|
||||||
|
.then(function (response) {
|
||||||
|
self.tickets = response.data.map(function (obj) {
|
||||||
|
console.log(obj)
|
||||||
|
return mapEvents(obj)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created: function () {
|
||||||
|
this.getEventTickets()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
{% assets filters='rjsmin', output='__bundle__/core/chart.js',
|
||||||
|
'vendor/moment@2.25.1/moment.min.js', 'vendor/chart.js@2.9.3/chart.min.js' %}
|
||||||
|
<script type="text/javascript" src="{{ ASSET_URL }}"></script>
|
||||||
|
{% endassets %} {% assets filters='rjsmin', output='__bundle__/core/wallet.js',
|
||||||
|
'vendor/bolt11/utils.js', 'vendor/bolt11/decoder.js',
|
||||||
|
'vendor/vue-qrcode-reader@2.2.0/vue-qrcode-reader.min.js' %}
|
||||||
|
<script type="text/javascript" src="{{ ASSET_URL }}"></script>
|
||||||
|
{% endassets %} {% endblock %}
|
|
@ -1,670 +0,0 @@
|
||||||
<!-- @format -->
|
|
||||||
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8" />
|
|
||||||
<title>LNBits Wallet</title>
|
|
||||||
<meta
|
|
||||||
content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"
|
|
||||||
name="viewport"
|
|
||||||
/>
|
|
||||||
<!-- Bootstrap 3.3.2 -->
|
|
||||||
<link
|
|
||||||
rel="stylesheet"
|
|
||||||
media="screen"
|
|
||||||
href="{{ url_for('static', filename='bootstrap/css/bootstrap.min.css') }}"
|
|
||||||
/>
|
|
||||||
<!-- FontAwesome 4.3.0 -->
|
|
||||||
<link
|
|
||||||
href="https://maxcdn.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.min.css"
|
|
||||||
rel="stylesheet"
|
|
||||||
type="text/css"
|
|
||||||
/>
|
|
||||||
<!-- Ionicons 2.0.0 -->
|
|
||||||
<link
|
|
||||||
href="https://code.ionicframework.com/ionicons/2.0.0/css/ionicons.min.css"
|
|
||||||
rel="stylesheet"
|
|
||||||
type="text/css"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<!-- Theme style -->
|
|
||||||
<link
|
|
||||||
rel="stylesheet"
|
|
||||||
media="screen"
|
|
||||||
href="{{ url_for('static', filename='dist/css/AdminLTE.min.css') }}"
|
|
||||||
/>
|
|
||||||
<!-- AdminLTE Skins. Choose a skin from the css/skins
|
|
||||||
folder instead of downloading all of them to reduce the load. -->
|
|
||||||
|
|
||||||
<link
|
|
||||||
rel="stylesheet"
|
|
||||||
media="screen"
|
|
||||||
href="{{ url_for('static', filename='dist/css/skins/_all-skins.min.css') }}"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<!-- Morris chart -->
|
|
||||||
<link
|
|
||||||
rel="stylesheet"
|
|
||||||
media="screen"
|
|
||||||
href="{{ url_for('static', filename='plugins/morris/morris.css') }}"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<!-- jvectormap -->
|
|
||||||
<link
|
|
||||||
rel="stylesheet"
|
|
||||||
media="screen"
|
|
||||||
href="{{ url_for('static', filename='plugins/jvectormap/jquery-jvectormap-1.2.2.css') }}"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<!-- bootstrap wysihtml5 - text editor -->
|
|
||||||
<link
|
|
||||||
rel="stylesheet"
|
|
||||||
media="screen"
|
|
||||||
href="{{ url_for('static', filename='plugins/bootstrap-wysihtml5/bootstrap3-wysihtml5.min.css') }}"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->
|
|
||||||
<!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
|
|
||||||
<!--[if lt IE 9]>
|
|
||||||
<script src="https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
|
|
||||||
<script src="https://oss.maxcdn.com/libs/respond.js/1.3.0/respond.min.js"></script>
|
|
||||||
<![endif]-->
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.small-box > .small-box-footer {
|
|
||||||
text-align: left;
|
|
||||||
padding-left: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#loadingMessage {
|
|
||||||
text-align: center;
|
|
||||||
padding: 40px;
|
|
||||||
background-color: #eee;
|
|
||||||
}
|
|
||||||
|
|
||||||
#canvas {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
#output {
|
|
||||||
margin-top: 20px;
|
|
||||||
background: #eee;
|
|
||||||
padding: 10px;
|
|
||||||
padding-bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#output div {
|
|
||||||
padding-bottom: 10px;
|
|
||||||
word-wrap: break-word;
|
|
||||||
}
|
|
||||||
|
|
||||||
#noQRFound {
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<!-- jQuery 2.1.3 -->
|
|
||||||
<script src="{{ url_for('static', filename='plugins/jQuery/jQuery-2.1.3.min.js') }}"></script>
|
|
||||||
<!-- jQuery UI 1.11.2 -->
|
|
||||||
<script
|
|
||||||
src="https://code.jquery.com/ui/1.11.2/jquery-ui.min.js"
|
|
||||||
type="text/javascript"
|
|
||||||
></script>
|
|
||||||
<!-- Resolve conflict in jQuery UI tooltip with Bootstrap tooltip -->
|
|
||||||
<script>
|
|
||||||
$.widget.bridge('uibutton', $.ui.button)
|
|
||||||
</script>
|
|
||||||
<!-- Bootstrap 3.3.2 JS -->
|
|
||||||
<script
|
|
||||||
src="{{ url_for('static', filename='bootstrap/js/bootstrap.min.js') }}"
|
|
||||||
type="text/javascript"
|
|
||||||
></script>
|
|
||||||
<!-- Morris.js charts -->
|
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/raphael/2.1.0/raphael-min.js"></script>
|
|
||||||
<script
|
|
||||||
src="{{ url_for('static', filename='plugins/morris/morris.min.js') }}"
|
|
||||||
type="text/javascript"
|
|
||||||
></script>
|
|
||||||
<!-- Sparkline -->
|
|
||||||
<script
|
|
||||||
src="{{ url_for('static', filename='plugins/sparkline/jquery.sparkline.min.js') }}"
|
|
||||||
type="text/javascript"
|
|
||||||
></script>
|
|
||||||
<!-- jvectormap -->
|
|
||||||
<script
|
|
||||||
src="{{ url_for('static', filename='plugins/jvectormap/jquery-jvectormap-1.2.2.min.js') }}"
|
|
||||||
type="text/javascript"
|
|
||||||
></script>
|
|
||||||
<script
|
|
||||||
src="{{ url_for('static', filename='plugins/jvectormap/jquery-jvectormap-world-mill-en.js') }}"
|
|
||||||
type="text/javascript"
|
|
||||||
></script>
|
|
||||||
<!-- jQuery Knob Chart -->
|
|
||||||
<script
|
|
||||||
src="{{ url_for('static', filename='plugins/knob/jquery.knob.js') }}"
|
|
||||||
type="text/javascript"
|
|
||||||
></script>
|
|
||||||
<!-- Bootstrap WYSIHTML5 -->
|
|
||||||
<script
|
|
||||||
src="{{ url_for('static', filename='plugins/bootstrap-wysihtml5/bootstrap3-wysihtml5.all.min.js') }}"
|
|
||||||
type="text/javascript"
|
|
||||||
></script>
|
|
||||||
<!-- Slimscroll -->
|
|
||||||
<script
|
|
||||||
src="{{ url_for('static', filename='plugins/slimScroll/jquery.slimscroll.min.js') }}"
|
|
||||||
type="text/javascript"
|
|
||||||
></script>
|
|
||||||
<!-- FastClick -->
|
|
||||||
<script src="{{ url_for('static', filename='plugins/fastclick/fastclick.min.js') }}"></script>
|
|
||||||
<!-- AdminLTE App -->
|
|
||||||
<script
|
|
||||||
src="{{ url_for('static', filename='dist/js/app.min.js') }}"
|
|
||||||
type="text/javascript"
|
|
||||||
></script>
|
|
||||||
|
|
||||||
<!-- AdminLTE dashboard demo (This is only for demo purposes) -->
|
|
||||||
<script
|
|
||||||
src="{{ url_for('static', filename='dist/js/pages/dashboard.js') }}"
|
|
||||||
type="text/javascript"
|
|
||||||
></script>
|
|
||||||
|
|
||||||
<!-- AdminLTE for demo purposes -->
|
|
||||||
<script
|
|
||||||
src="{{ url_for('static', filename='dist/js/demo.js') }}"
|
|
||||||
type="text/javascript"
|
|
||||||
></script>
|
|
||||||
|
|
||||||
<script
|
|
||||||
src="{{ url_for('static', filename='plugins/datatables/jquery.dataTables.js') }}"
|
|
||||||
type="text/javascript"
|
|
||||||
></script>
|
|
||||||
<link
|
|
||||||
rel="stylesheet"
|
|
||||||
href="//cdnjs.cloudflare.com/ajax/libs/morris.js/0.5.1/morris.css"
|
|
||||||
/>
|
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/raphael/2.1.0/raphael-min.js"></script>
|
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/morris.js/0.5.1/morris.min.js"></script>
|
|
||||||
<script
|
|
||||||
src="{{ url_for('static', filename='plugins/jscam/JS.js') }}"
|
|
||||||
type="text/javascript"
|
|
||||||
></script>
|
|
||||||
<script
|
|
||||||
src="{{ url_for('static', filename='plugins/jscam/qrcode.min.js') }}"
|
|
||||||
type="text/javascript"
|
|
||||||
></script>
|
|
||||||
<script
|
|
||||||
src="{{ url_for('static', filename='plugins/bolt11/decoder.js') }}"
|
|
||||||
type="text/javascript"
|
|
||||||
></script>
|
|
||||||
<script
|
|
||||||
src="{{ url_for('static', filename='plugins/bolt11/utils.js') }}"
|
|
||||||
type="text/javascript"
|
|
||||||
></script>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
//GOOFY CSS HACK TO GO DARK
|
|
||||||
|
|
||||||
.skin-blue .wrapper {
|
|
||||||
background: #1f2234;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
color: #fff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.skin-blue .sidebar-menu > li.active > a {
|
|
||||||
color: #fff;
|
|
||||||
background: #1f2234;
|
|
||||||
border-left-color: #8964a9;
|
|
||||||
}
|
|
||||||
|
|
||||||
.skin-blue .main-header .navbar {
|
|
||||||
background-color: #2e507d;
|
|
||||||
}
|
|
||||||
|
|
||||||
.content-wrapper,
|
|
||||||
.right-side {
|
|
||||||
background-color: #1f2234;
|
|
||||||
}
|
|
||||||
.skin-blue .main-header .logo {
|
|
||||||
background-color: #1f2234;
|
|
||||||
color: #fff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.skin-blue .sidebar-menu > li.header {
|
|
||||||
color: #4b646f;
|
|
||||||
background: #1f2234;
|
|
||||||
}
|
|
||||||
.skin-blue .wrapper,
|
|
||||||
.skin-blue .main-sidebar,
|
|
||||||
.skin-blue .left-side {
|
|
||||||
background: #1f2234;
|
|
||||||
}
|
|
||||||
|
|
||||||
.skin-blue .sidebar-menu > li > .treeview-menu {
|
|
||||||
margin: 0 1px;
|
|
||||||
background: #1f2234;
|
|
||||||
}
|
|
||||||
|
|
||||||
.skin-blue .sidebar-menu > li > a {
|
|
||||||
border-left: 3px solid transparent;
|
|
||||||
margin-right: 1px;
|
|
||||||
}
|
|
||||||
.skin-blue .sidebar-menu > li > a:hover,
|
|
||||||
.skin-blue .sidebar-menu > li.active > a {
|
|
||||||
color: #fff;
|
|
||||||
background: #3e355a;
|
|
||||||
border-left-color: #8964a9;
|
|
||||||
}
|
|
||||||
|
|
||||||
.skin-blue .main-header .logo:hover {
|
|
||||||
background: #3e355a;
|
|
||||||
}
|
|
||||||
|
|
||||||
.skin-blue .main-header .navbar .sidebar-toggle:hover {
|
|
||||||
background-color: #3e355a;
|
|
||||||
}
|
|
||||||
.main-footer {
|
|
||||||
background-color: #1f2234;
|
|
||||||
padding: 15px;
|
|
||||||
color: #fff;
|
|
||||||
border-top: 0px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.skin-blue .main-header .navbar {
|
|
||||||
background-color: #1f2234;
|
|
||||||
}
|
|
||||||
|
|
||||||
.bg-red,
|
|
||||||
.callout.callout-danger,
|
|
||||||
.alert-danger,
|
|
||||||
.alert-error,
|
|
||||||
.label-danger,
|
|
||||||
.modal-danger .modal-body {
|
|
||||||
background-color: #1f2234 !important;
|
|
||||||
}
|
|
||||||
.alert-danger,
|
|
||||||
.alert-error {
|
|
||||||
border-color: #fff;
|
|
||||||
border: 1px solid #fff;
|
|
||||||
border-radius: 7px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.skin-blue .main-header .navbar .nav > li > a:hover,
|
|
||||||
.skin-blue .main-header .navbar .nav > li > a:active,
|
|
||||||
.skin-blue .main-header .navbar .nav > li > a:focus,
|
|
||||||
.skin-blue .main-header .navbar .nav .open > a,
|
|
||||||
.skin-blue .main-header .navbar .nav .open > a:hover,
|
|
||||||
.skin-blue .main-header .navbar .nav .open > a:focus {
|
|
||||||
color: #f6f6f6;
|
|
||||||
background-color: #3e355a;
|
|
||||||
}
|
|
||||||
.bg-aqua,
|
|
||||||
.callout.callout-info,
|
|
||||||
.alert-info,
|
|
||||||
.label-info,
|
|
||||||
.modal-info .modal-body {
|
|
||||||
background-color: #3e355a !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.box {
|
|
||||||
position: relative;
|
|
||||||
border-radius: 3px;
|
|
||||||
background-color: #333646;
|
|
||||||
border-top: 3px solid #8964a9;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
.table-striped > tbody > tr:nth-of-type(2n + 1) {
|
|
||||||
background-color: #333646;
|
|
||||||
}
|
|
||||||
|
|
||||||
.box-header {
|
|
||||||
color: #fff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.box.box-danger {
|
|
||||||
border-top-color: #8964a9;
|
|
||||||
}
|
|
||||||
.box.box-primary {
|
|
||||||
border-top-color: #8964a9;
|
|
||||||
}
|
|
||||||
|
|
||||||
a {
|
|
||||||
color: #8964a9;
|
|
||||||
}
|
|
||||||
.box-header.with-border {
|
|
||||||
border-bottom: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
a:hover,
|
|
||||||
a:active,
|
|
||||||
a:focus {
|
|
||||||
outline: none;
|
|
||||||
text-decoration: none;
|
|
||||||
color: #fff;
|
|
||||||
}
|
|
||||||
// .modal.in .modal-dialog{
|
|
||||||
// color:#000;
|
|
||||||
// }
|
|
||||||
|
|
||||||
.form-control {
|
|
||||||
background-color: #333646;
|
|
||||||
color: #fff;
|
|
||||||
}
|
|
||||||
.box-footer {
|
|
||||||
border-top: none;
|
|
||||||
|
|
||||||
background-color: #333646;
|
|
||||||
}
|
|
||||||
.modal-footer {
|
|
||||||
border-top: none;
|
|
||||||
}
|
|
||||||
.modal-content {
|
|
||||||
background-color: #333646;
|
|
||||||
}
|
|
||||||
.modal.in .modal-dialog {
|
|
||||||
background-color: #333646;
|
|
||||||
}
|
|
||||||
|
|
||||||
.layout-boxed {
|
|
||||||
background: none;
|
|
||||||
background-color: rgba(0, 0, 0, 0);
|
|
||||||
background-color: #3e355a;
|
|
||||||
}
|
|
||||||
|
|
||||||
.skin-blue .sidebar-menu > li > a:hover,
|
|
||||||
.skin-blue .sidebar-menu > li.active > a {
|
|
||||||
background: none;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body class="skin-blue layout-boxed sidebar-collapse sidebar-open">
|
|
||||||
<div class="wrapper">
|
|
||||||
<header class="main-header">
|
|
||||||
<!-- Logo -->
|
|
||||||
<a href="{{ url_for('core.home') }}" class="logo"><b>LN</b>bits</a>
|
|
||||||
<!-- Header Navbar: style can be found in header.less -->
|
|
||||||
<nav class="navbar navbar-static-top" role="navigation">
|
|
||||||
<!-- Sidebar toggle button-->
|
|
||||||
<a
|
|
||||||
href="#"
|
|
||||||
class="sidebar-toggle"
|
|
||||||
data-toggle="offcanvas"
|
|
||||||
role="button"
|
|
||||||
>
|
|
||||||
<span class="sr-only">Toggle navigation</span>
|
|
||||||
</a>
|
|
||||||
<div class="navbar-custom-menu">
|
|
||||||
<ul class="nav navbar-nav">
|
|
||||||
<!-- Messages: style can be found in dropdown.less-->
|
|
||||||
<li class="dropdown messages-menu">
|
|
||||||
{% block messages %}{% endblock %}
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</nav>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
<aside class="main-sidebar">
|
|
||||||
<!-- sidebar: style can be found in sidebar.less -->
|
|
||||||
<section class="sidebar" style="height: auto;">
|
|
||||||
<!-- Sidebar user panel -->
|
|
||||||
</section>
|
|
||||||
<!-- /.sidebar -->
|
|
||||||
</aside>
|
|
||||||
|
|
||||||
<!-- Right side column. Contains the navbar and content of the page -->
|
|
||||||
<div class="content-wrapper">
|
|
||||||
<!-- Content Header (Page header) -->
|
|
||||||
<section class="content-header">
|
|
||||||
<h1>
|
|
||||||
LNBits Events
|
|
||||||
<small>Lightning powered tickets</small>
|
|
||||||
</h1>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<!-- Main content -->
|
|
||||||
<section class="content">
|
|
||||||
<br /><br />
|
|
||||||
<center><h1 style="font-size: 500%;">{{ user_ev[0][6] }}</h1></center>
|
|
||||||
|
|
||||||
<br /><br /><br />
|
|
||||||
|
|
||||||
<div
|
|
||||||
class="modal fade sends"
|
|
||||||
tabindex="-1"
|
|
||||||
role="dialog"
|
|
||||||
aria-labelledby="myLargeModalLabel"
|
|
||||||
aria-hidden="true"
|
|
||||||
>
|
|
||||||
<div class="modal-dialog">
|
|
||||||
<div id="scantickets" style="padding: 0 10px 0 10px;"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<center>
|
|
||||||
<button
|
|
||||||
onclick="scanQRsend()"
|
|
||||||
class="btn btn-block btn-primary btn-lg"
|
|
||||||
data-toggle="modal"
|
|
||||||
data-target=".sends"
|
|
||||||
style="width: 300px;"
|
|
||||||
>
|
|
||||||
Scan ticket
|
|
||||||
</button>
|
|
||||||
</center>
|
|
||||||
|
|
||||||
<div id="scantickets"></div>
|
|
||||||
<br /><br /><br />
|
|
||||||
<div id="qrcodetxt"></div>
|
|
||||||
<br />
|
|
||||||
<br /><br /><br />
|
|
||||||
|
|
||||||
<center>
|
|
||||||
<div class="row" style="width: 80%; margin-top: 80px;">
|
|
||||||
<style>
|
|
||||||
.ema,
|
|
||||||
button:focus .txt {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
button:focus .ema {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
<div class="box">
|
|
||||||
<div class="box-header">
|
|
||||||
<h3 class="box-title">Attendees<b id="withdraws"></b></h3>
|
|
||||||
</div>
|
|
||||||
<!-- /.box-header -->
|
|
||||||
<div class="box-body no-padding">
|
|
||||||
<table
|
|
||||||
id="pagnation"
|
|
||||||
class="table table-bswearing anchorordered table-striped"
|
|
||||||
>
|
|
||||||
<tr>
|
|
||||||
<th style="width: 20%;">Name</th>
|
|
||||||
<th style="width: 20%;">Email</th>
|
|
||||||
<th style="width: 50%;">Ticket</th>
|
|
||||||
<th style="width: 10%;">Registered</th>
|
|
||||||
</tr>
|
|
||||||
<tbody id="ticketwaves"></tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
<!-- /.box-body -->
|
|
||||||
</div>
|
|
||||||
<!-- /.box -->
|
|
||||||
</div>
|
|
||||||
</center>
|
|
||||||
</section>
|
|
||||||
<!-- /.content -->
|
|
||||||
</div>
|
|
||||||
<!-- /.content-wrapper -->
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
|
|
||||||
|
|
||||||
window.user_ev = {{ user_ev | tojson | safe }}
|
|
||||||
window.user_ev_sold = {{ user_ev_sold | tojson | safe }}
|
|
||||||
console.log(user_ev)
|
|
||||||
console.log(user_ev_sold)
|
|
||||||
|
|
||||||
|
|
||||||
function drawChart(user_ev_sold) {
|
|
||||||
var transactionsHTML = ''
|
|
||||||
|
|
||||||
for (var i = 0; i < user_ev_sold.length; i++) {
|
|
||||||
var ev = user_ev_sold[i]
|
|
||||||
|
|
||||||
transactionsHTML =
|
|
||||||
"<tr><td>" +
|
|
||||||
ev.name +
|
|
||||||
'</td><td>' +
|
|
||||||
'<button style="background-color: #333646;padding: 0;border: none;background: none;" class="lost"><span class="txt">xxxxxx</span>' +
|
|
||||||
'<span class="ema">' + ev.email + '</span></button>'+
|
|
||||||
'</td><td>' +
|
|
||||||
ev.hash +
|
|
||||||
'</td><td>' +
|
|
||||||
ev.reg +
|
|
||||||
'</td></tr>' +
|
|
||||||
transactionsHTML
|
|
||||||
document.getElementById('ticketwaves').innerHTML = transactionsHTML
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (user_ev_sold.length) {
|
|
||||||
drawChart(user_ev_sold)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
function postAjax(url, data, thekey, success) {
|
|
||||||
var params =
|
|
||||||
typeof data == 'string'
|
|
||||||
? data
|
|
||||||
: Object.keys(data)
|
|
||||||
.map(function(k) {
|
|
||||||
return encodeURIComponent(k) + '=' + encodeURIComponent(data[k])
|
|
||||||
})
|
|
||||||
.join('&')
|
|
||||||
var xhr = window.XMLHttpRequest
|
|
||||||
? new XMLHttpRequest()
|
|
||||||
: new ActiveXObject('Microsoft.XMLHTTP')
|
|
||||||
xhr.open('POST', url)
|
|
||||||
xhr.onreadystatechange = function() {
|
|
||||||
if (xhr.readyState > 3 && xhr.status == 200) {
|
|
||||||
success(xhr.responseText)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
xhr.setRequestHeader('X-Api-Key', thekey)
|
|
||||||
xhr.setRequestHeader('Content-Type', 'application/json')
|
|
||||||
xhr.send(params)
|
|
||||||
return xhr
|
|
||||||
}
|
|
||||||
|
|
||||||
function getAjax(url, thekey, success) {
|
|
||||||
var xhr = window.XMLHttpRequest
|
|
||||||
? new XMLHttpRequest()
|
|
||||||
: new ActiveXObject('Microsoft.XMLHTTP')
|
|
||||||
xhr.open('GET', url, true)
|
|
||||||
xhr.onreadystatechange = function() {
|
|
||||||
if (xhr.readyState > 3 && xhr.status == 200) {
|
|
||||||
success(xhr.responseText)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
xhr.setRequestHeader('X-Api-Key', thekey)
|
|
||||||
xhr.setRequestHeader('Content-Type', 'application/json')
|
|
||||||
|
|
||||||
xhr.send()
|
|
||||||
return xhr
|
|
||||||
}
|
|
||||||
|
|
||||||
function scanQRsend() {
|
|
||||||
document.getElementById('scantickets').innerHTML =
|
|
||||||
"<div class='modal-content'>"+
|
|
||||||
"<br/><div id='registered'><div id='loadingMessage'>🎥 Unable to access video stream (please make sure you have a webcam enabled)</div>" +
|
|
||||||
"<canvas id='canvas' hidden></canvas><div id='output' hidden><div id='outputMessage'></div>" +
|
|
||||||
"<br/><span id='outputData'></span></div></div><div class='modal-footer'>"+
|
|
||||||
"<button type='submit' class='btn btn-primary' onclick='cancelsend()'>Cancel</button><br/><br/></div>"
|
|
||||||
var video = document.createElement('video')
|
|
||||||
var canvasElement = document.getElementById('canvas')
|
|
||||||
var canvas = canvasElement.getContext('2d')
|
|
||||||
var loadingMessage = document.getElementById('loadingMessage')
|
|
||||||
var outputContainer = document.getElementById('output')
|
|
||||||
var outputMessage = document.getElementById('outputMessage')
|
|
||||||
var outputData = document.getElementById('outputData')
|
|
||||||
|
|
||||||
// Use facingMode: environment to attemt to get the front camera on phones
|
|
||||||
navigator.mediaDevices
|
|
||||||
.getUserMedia({video: {facingMode: 'environment'}})
|
|
||||||
.then(function(stream) {
|
|
||||||
video.srcObject = stream
|
|
||||||
video.setAttribute('playsinline', true) // required to tell iOS safari we don't want fullscreen
|
|
||||||
video.play()
|
|
||||||
requestAnimationFrame(tick)
|
|
||||||
})
|
|
||||||
|
|
||||||
function tick() {
|
|
||||||
loadingMessage.innerText = '⌛ Loading video...'
|
|
||||||
if (video.readyState === video.HAVE_ENOUGH_DATA) {
|
|
||||||
loadingMessage.hidden = true
|
|
||||||
canvasElement.hidden = false
|
|
||||||
outputContainer.hidden = false
|
|
||||||
canvasElement.height = video.videoHeight
|
|
||||||
canvasElement.width = video.videoWidth
|
|
||||||
canvas.drawImage(video, 0, 0, canvasElement.width, canvasElement.height)
|
|
||||||
var imageData = canvas.getImageData(
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
canvasElement.width,
|
|
||||||
canvasElement.height
|
|
||||||
)
|
|
||||||
var code = jsQR(imageData.data, imageData.width, imageData.height, {
|
|
||||||
inversionAttempts: 'dontInvert'
|
|
||||||
})
|
|
||||||
if (code) {
|
|
||||||
thehash = code.data
|
|
||||||
document.getElementById('registered').innerHTML = "<h1 style='color:green;font-size:300%;'>Registered!</h1>"
|
|
||||||
outputMessage.hidden = true
|
|
||||||
outputData.parentElement.hidden = false
|
|
||||||
outputData.innerText = JSON.stringify(code.data)
|
|
||||||
getAjax("{{ url_for('events.api_checkticket') }}?thehash=" + thehash, "filla", function(datab) {
|
|
||||||
if (JSON.parse(datab).status == 'TRUE') {
|
|
||||||
location.reload()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
} else {
|
|
||||||
outputMessage.hidden = false
|
|
||||||
outputData.parentElement.hidden = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
requestAnimationFrame(tick)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function cancelsend() {
|
|
||||||
location.reload();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function getUrlVars() {
|
|
||||||
var vars = {};
|
|
||||||
var parts = window.location.href.replace(/[?&]+([^=&]+)=([^&]*)/gi, function(m,key,value) {
|
|
||||||
vars[key] = value;
|
|
||||||
});
|
|
||||||
return vars;
|
|
||||||
}
|
|
||||||
var name = getUrlVars()["name"];
|
|
||||||
var thehash = getUrlVars()["thehash"];
|
|
||||||
console.log(thehash)
|
|
||||||
if(thehash != null){
|
|
||||||
document.getElementById('qrcodetxt').innerHTML = "<center><h1>" + name + " is registered!</h1></center>"
|
|
||||||
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
</html>
|
|
|
@ -1,457 +1,33 @@
|
||||||
<!-- @format -->
|
{% extends "public.html" %} {% block page %}
|
||||||
|
<div class="row q-col-gutter-md justify-center">
|
||||||
<!DOCTYPE html>
|
<div class="col-12 col-md-7 col-lg-6 q-gutter-y-md">
|
||||||
<html>
|
<q-card class="q-pa-lg">
|
||||||
<head>
|
<q-card-section class="q-pa-none">
|
||||||
<meta charset="UTF-8" />
|
|
||||||
<title>LNBits Wallet</title>
|
|
||||||
<meta
|
|
||||||
content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"
|
|
||||||
name="viewport"
|
|
||||||
/>
|
|
||||||
<!-- Bootstrap 3.3.2 -->
|
|
||||||
<link
|
|
||||||
rel="stylesheet"
|
|
||||||
media="screen"
|
|
||||||
href="{{ url_for('static', filename='bootstrap/css/bootstrap.min.css') }}"
|
|
||||||
/>
|
|
||||||
<!-- FontAwesome 4.3.0 -->
|
|
||||||
<link
|
|
||||||
href="https://maxcdn.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.min.css"
|
|
||||||
rel="stylesheet"
|
|
||||||
type="text/css"
|
|
||||||
/>
|
|
||||||
<!-- Ionicons 2.0.0 -->
|
|
||||||
<link
|
|
||||||
href="https://code.ionicframework.com/ionicons/2.0.0/css/ionicons.min.css"
|
|
||||||
rel="stylesheet"
|
|
||||||
type="text/css"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<!-- Theme style -->
|
|
||||||
<link
|
|
||||||
rel="stylesheet"
|
|
||||||
media="screen"
|
|
||||||
href="{{ url_for('static', filename='dist/css/AdminLTE.min.css') }}"
|
|
||||||
/>
|
|
||||||
<!-- AdminLTE Skins. Choose a skin from the css/skins
|
|
||||||
folder instead of downloading all of them to reduce the load. -->
|
|
||||||
|
|
||||||
<link
|
|
||||||
rel="stylesheet"
|
|
||||||
media="screen"
|
|
||||||
href="{{ url_for('static', filename='dist/css/skins/_all-skins.min.css') }}"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<!-- Morris chart -->
|
|
||||||
<link
|
|
||||||
rel="stylesheet"
|
|
||||||
media="screen"
|
|
||||||
href="{{ url_for('static', filename='plugins/morris/morris.css') }}"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<!-- jvectormap -->
|
|
||||||
<link
|
|
||||||
rel="stylesheet"
|
|
||||||
media="screen"
|
|
||||||
href="{{ url_for('static', filename='plugins/jvectormap/jquery-jvectormap-1.2.2.css') }}"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<!-- bootstrap wysihtml5 - text editor -->
|
|
||||||
<link
|
|
||||||
rel="stylesheet"
|
|
||||||
media="screen"
|
|
||||||
href="{{ url_for('static', filename='plugins/bootstrap-wysihtml5/bootstrap3-wysihtml5.min.css') }}"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->
|
|
||||||
<!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
|
|
||||||
<!--[if lt IE 9]>
|
|
||||||
<script src="https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
|
|
||||||
<script src="https://oss.maxcdn.com/libs/respond.js/1.3.0/respond.min.js"></script>
|
|
||||||
<![endif]-->
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.small-box > .small-box-footer {
|
|
||||||
text-align: left;
|
|
||||||
padding-left: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#loadingMessage {
|
|
||||||
text-align: center;
|
|
||||||
padding: 40px;
|
|
||||||
background-color: #eee;
|
|
||||||
}
|
|
||||||
|
|
||||||
#canvas {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
#output {
|
|
||||||
margin-top: 20px;
|
|
||||||
background: #eee;
|
|
||||||
padding: 10px;
|
|
||||||
padding-bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#output div {
|
|
||||||
padding-bottom: 10px;
|
|
||||||
word-wrap: break-word;
|
|
||||||
}
|
|
||||||
|
|
||||||
#noQRFound {
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<!-- jQuery 2.1.3 -->
|
|
||||||
<script src="{{ url_for('static', filename='plugins/jQuery/jQuery-2.1.3.min.js') }}"></script>
|
|
||||||
<!-- jQuery UI 1.11.2 -->
|
|
||||||
<script
|
|
||||||
src="https://code.jquery.com/ui/1.11.2/jquery-ui.min.js"
|
|
||||||
type="text/javascript"
|
|
||||||
></script>
|
|
||||||
<!-- Resolve conflict in jQuery UI tooltip with Bootstrap tooltip -->
|
|
||||||
<script>
|
|
||||||
$.widget.bridge('uibutton', $.ui.button)
|
|
||||||
</script>
|
|
||||||
<!-- Bootstrap 3.3.2 JS -->
|
|
||||||
<script
|
|
||||||
src="{{ url_for('static', filename='bootstrap/js/bootstrap.min.js') }}"
|
|
||||||
type="text/javascript"
|
|
||||||
></script>
|
|
||||||
<!-- Morris.js charts -->
|
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/raphael/2.1.0/raphael-min.js"></script>
|
|
||||||
<script
|
|
||||||
src="{{ url_for('static', filename='plugins/morris/morris.min.js') }}"
|
|
||||||
type="text/javascript"
|
|
||||||
></script>
|
|
||||||
<!-- Sparkline -->
|
|
||||||
<script
|
|
||||||
src="{{ url_for('static', filename='plugins/sparkline/jquery.sparkline.min.js') }}"
|
|
||||||
type="text/javascript"
|
|
||||||
></script>
|
|
||||||
<!-- jvectormap -->
|
|
||||||
<script
|
|
||||||
src="{{ url_for('static', filename='plugins/jvectormap/jquery-jvectormap-1.2.2.min.js') }}"
|
|
||||||
type="text/javascript"
|
|
||||||
></script>
|
|
||||||
<script
|
|
||||||
src="{{ url_for('static', filename='plugins/jvectormap/jquery-jvectormap-world-mill-en.js') }}"
|
|
||||||
type="text/javascript"
|
|
||||||
></script>
|
|
||||||
<!-- jQuery Knob Chart -->
|
|
||||||
<script
|
|
||||||
src="{{ url_for('static', filename='plugins/knob/jquery.knob.js') }}"
|
|
||||||
type="text/javascript"
|
|
||||||
></script>
|
|
||||||
<!-- Bootstrap WYSIHTML5 -->
|
|
||||||
<script
|
|
||||||
src="{{ url_for('static', filename='plugins/bootstrap-wysihtml5/bootstrap3-wysihtml5.all.min.js') }}"
|
|
||||||
type="text/javascript"
|
|
||||||
></script>
|
|
||||||
<!-- Slimscroll -->
|
|
||||||
<script
|
|
||||||
src="{{ url_for('static', filename='plugins/slimScroll/jquery.slimscroll.min.js') }}"
|
|
||||||
type="text/javascript"
|
|
||||||
></script>
|
|
||||||
<!-- FastClick -->
|
|
||||||
<script src="{{ url_for('static', filename='plugins/fastclick/fastclick.min.js') }}"></script>
|
|
||||||
<!-- AdminLTE App -->
|
|
||||||
<script
|
|
||||||
src="{{ url_for('static', filename='dist/js/app.min.js') }}"
|
|
||||||
type="text/javascript"
|
|
||||||
></script>
|
|
||||||
|
|
||||||
<!-- AdminLTE dashboard demo (This is only for demo purposes) -->
|
|
||||||
<script
|
|
||||||
src="{{ url_for('static', filename='dist/js/pages/dashboard.js') }}"
|
|
||||||
type="text/javascript"
|
|
||||||
></script>
|
|
||||||
|
|
||||||
<!-- AdminLTE for demo purposes -->
|
|
||||||
<script
|
|
||||||
src="{{ url_for('static', filename='dist/js/demo.js') }}"
|
|
||||||
type="text/javascript"
|
|
||||||
></script>
|
|
||||||
|
|
||||||
<script
|
|
||||||
src="{{ url_for('static', filename='plugins/datatables/jquery.dataTables.js') }}"
|
|
||||||
type="text/javascript"
|
|
||||||
></script>
|
|
||||||
<link
|
|
||||||
rel="stylesheet"
|
|
||||||
href="//cdnjs.cloudflare.com/ajax/libs/morris.js/0.5.1/morris.css"
|
|
||||||
/>
|
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/raphael/2.1.0/raphael-min.js"></script>
|
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/morris.js/0.5.1/morris.min.js"></script>
|
|
||||||
<script
|
|
||||||
src="{{ url_for('static', filename='plugins/jscam/JS.js') }}"
|
|
||||||
type="text/javascript"
|
|
||||||
></script>
|
|
||||||
<script
|
|
||||||
src="{{ url_for('static', filename='plugins/jscam/qrcode.min.js') }}"
|
|
||||||
type="text/javascript"
|
|
||||||
></script>
|
|
||||||
<script
|
|
||||||
src="{{ url_for('static', filename='plugins/bolt11/decoder.js') }}"
|
|
||||||
type="text/javascript"
|
|
||||||
></script>
|
|
||||||
<script
|
|
||||||
src="{{ url_for('static', filename='plugins/bolt11/utils.js') }}"
|
|
||||||
type="text/javascript"
|
|
||||||
></script>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
//GOOFY CSS HACK TO GO DARK
|
|
||||||
|
|
||||||
.skin-blue .wrapper {
|
|
||||||
background: #1f2234;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
color: #fff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.skin-blue .sidebar-menu > li.active > a {
|
|
||||||
color: #fff;
|
|
||||||
background: #1f2234;
|
|
||||||
border-left-color: #8964a9;
|
|
||||||
}
|
|
||||||
|
|
||||||
.skin-blue .main-header .navbar {
|
|
||||||
background-color: #2e507d;
|
|
||||||
}
|
|
||||||
|
|
||||||
.content-wrapper,
|
|
||||||
.right-side {
|
|
||||||
background-color: #1f2234;
|
|
||||||
}
|
|
||||||
.skin-blue .main-header .logo {
|
|
||||||
background-color: #1f2234;
|
|
||||||
color: #fff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.skin-blue .sidebar-menu > li.header {
|
|
||||||
color: #4b646f;
|
|
||||||
background: #1f2234;
|
|
||||||
}
|
|
||||||
.skin-blue .wrapper,
|
|
||||||
.skin-blue .main-sidebar,
|
|
||||||
.skin-blue .left-side {
|
|
||||||
background: #1f2234;
|
|
||||||
}
|
|
||||||
|
|
||||||
.skin-blue .sidebar-menu > li > .treeview-menu {
|
|
||||||
margin: 0 1px;
|
|
||||||
background: #1f2234;
|
|
||||||
}
|
|
||||||
|
|
||||||
.skin-blue .sidebar-menu > li > a {
|
|
||||||
border-left: 3px solid transparent;
|
|
||||||
margin-right: 1px;
|
|
||||||
}
|
|
||||||
.skin-blue .sidebar-menu > li > a:hover,
|
|
||||||
.skin-blue .sidebar-menu > li.active > a {
|
|
||||||
color: #fff;
|
|
||||||
background: #3e355a;
|
|
||||||
border-left-color: #8964a9;
|
|
||||||
}
|
|
||||||
|
|
||||||
.skin-blue .main-header .logo:hover {
|
|
||||||
background: #3e355a;
|
|
||||||
}
|
|
||||||
|
|
||||||
.skin-blue .main-header .navbar .sidebar-toggle:hover {
|
|
||||||
background-color: #3e355a;
|
|
||||||
}
|
|
||||||
.main-footer {
|
|
||||||
background-color: #1f2234;
|
|
||||||
padding: 15px;
|
|
||||||
color: #fff;
|
|
||||||
border-top: 0px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.skin-blue .main-header .navbar {
|
|
||||||
background-color: #1f2234;
|
|
||||||
}
|
|
||||||
|
|
||||||
.bg-red,
|
|
||||||
.callout.callout-danger,
|
|
||||||
.alert-danger,
|
|
||||||
.alert-error,
|
|
||||||
.label-danger,
|
|
||||||
.modal-danger .modal-body {
|
|
||||||
background-color: #1f2234 !important;
|
|
||||||
}
|
|
||||||
.alert-danger,
|
|
||||||
.alert-error {
|
|
||||||
border-color: #fff;
|
|
||||||
border: 1px solid #fff;
|
|
||||||
border-radius: 7px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.skin-blue .main-header .navbar .nav > li > a:hover,
|
|
||||||
.skin-blue .main-header .navbar .nav > li > a:active,
|
|
||||||
.skin-blue .main-header .navbar .nav > li > a:focus,
|
|
||||||
.skin-blue .main-header .navbar .nav .open > a,
|
|
||||||
.skin-blue .main-header .navbar .nav .open > a:hover,
|
|
||||||
.skin-blue .main-header .navbar .nav .open > a:focus {
|
|
||||||
color: #f6f6f6;
|
|
||||||
background-color: #3e355a;
|
|
||||||
}
|
|
||||||
.bg-aqua,
|
|
||||||
.callout.callout-info,
|
|
||||||
.alert-info,
|
|
||||||
.label-info,
|
|
||||||
.modal-info .modal-body {
|
|
||||||
background-color: #3e355a !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.box {
|
|
||||||
position: relative;
|
|
||||||
border-radius: 3px;
|
|
||||||
background-color: #333646;
|
|
||||||
border-top: 3px solid #8964a9;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
.table-striped > tbody > tr:nth-of-type(2n + 1) {
|
|
||||||
background-color: #333646;
|
|
||||||
}
|
|
||||||
|
|
||||||
.box-header {
|
|
||||||
color: #fff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.box.box-danger {
|
|
||||||
border-top-color: #8964a9;
|
|
||||||
}
|
|
||||||
.box.box-primary {
|
|
||||||
border-top-color: #8964a9;
|
|
||||||
}
|
|
||||||
|
|
||||||
a {
|
|
||||||
color: #8964a9;
|
|
||||||
}
|
|
||||||
.box-header.with-border {
|
|
||||||
border-bottom: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
a:hover,
|
|
||||||
a:active,
|
|
||||||
a:focus {
|
|
||||||
outline: none;
|
|
||||||
text-decoration: none;
|
|
||||||
color: #fff;
|
|
||||||
}
|
|
||||||
// .modal.in .modal-dialog{
|
|
||||||
// color:#000;
|
|
||||||
// }
|
|
||||||
|
|
||||||
.form-control {
|
|
||||||
background-color: #333646;
|
|
||||||
color: #fff;
|
|
||||||
}
|
|
||||||
.box-footer {
|
|
||||||
border-top: none;
|
|
||||||
|
|
||||||
background-color: #333646;
|
|
||||||
}
|
|
||||||
.modal-footer {
|
|
||||||
border-top: none;
|
|
||||||
}
|
|
||||||
.modal-content {
|
|
||||||
background-color: #333646;
|
|
||||||
}
|
|
||||||
.modal.in .modal-dialog {
|
|
||||||
background-color: #333646;
|
|
||||||
}
|
|
||||||
|
|
||||||
.layout-boxed {
|
|
||||||
background: none;
|
|
||||||
background-color: rgba(0, 0, 0, 0);
|
|
||||||
background-color: #3e355a;
|
|
||||||
}
|
|
||||||
|
|
||||||
.skin-blue .sidebar-menu > li > a:hover,
|
|
||||||
.skin-blue .sidebar-menu > li.active > a {
|
|
||||||
background: none;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body class="skin-blue layout-boxed sidebar-collapse sidebar-open">
|
|
||||||
<div class="wrapper">
|
|
||||||
<header class="main-header">
|
|
||||||
<!-- Logo -->
|
|
||||||
<a href="{{ url_for('core.home') }}" class="logo"><b>LN</b>bits</a>
|
|
||||||
<!-- Header Navbar: style can be found in header.less -->
|
|
||||||
<nav class="navbar navbar-static-top" role="navigation">
|
|
||||||
<!-- Sidebar toggle button-->
|
|
||||||
<a
|
|
||||||
href="#"
|
|
||||||
class="sidebar-toggle"
|
|
||||||
data-toggle="offcanvas"
|
|
||||||
role="button"
|
|
||||||
>
|
|
||||||
<span class="sr-only">Toggle navigation</span>
|
|
||||||
</a>
|
|
||||||
<div class="navbar-custom-menu">
|
|
||||||
<ul class="nav navbar-nav">
|
|
||||||
<!-- Messages: style can be found in dropdown.less-->
|
|
||||||
<li class="dropdown messages-menu">
|
|
||||||
{% block messages %}{% endblock %}
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</nav>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
<aside class="main-sidebar">
|
|
||||||
<!-- sidebar: style can be found in sidebar.less -->
|
|
||||||
<section class="sidebar" style="height: auto;"></section>
|
|
||||||
<!-- /.sidebar -->
|
|
||||||
</aside>
|
|
||||||
|
|
||||||
<!-- Right side column. Contains the navbar and content of the page -->
|
|
||||||
<div class="content-wrapper">
|
|
||||||
<!-- Content Header (Page header) -->
|
|
||||||
<section class="content-header">
|
|
||||||
<h1>
|
|
||||||
LNBits Events
|
|
||||||
<small>Lightning powered tickets</small>
|
|
||||||
</h1>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<!-- Main content -->
|
|
||||||
<section class="content">
|
|
||||||
<br /><br />
|
|
||||||
|
|
||||||
<center>
|
<center>
|
||||||
<h2 style="width: 70%; font-size: 400%;">
|
<h3 class="q-my-none">{{ ticket_name }} Ticket</h3>
|
||||||
Bookmark/Screenshot this page. <br />It is your ticket!
|
<br />
|
||||||
</h2>
|
<h5 class="q-my-none">
|
||||||
|
Bookmark or screenshot this page,<br />
|
||||||
|
and present it for registration!
|
||||||
|
</h5>
|
||||||
|
<br />
|
||||||
|
<qrcode
|
||||||
|
:value="{{ ticket_id }}"
|
||||||
|
:options="{width: 340}"
|
||||||
|
class="rounded-borders"
|
||||||
|
></qrcode>
|
||||||
</center>
|
</center>
|
||||||
<center>
|
</q-card-section>
|
||||||
<div
|
</q-card>
|
||||||
style="width: 340px; background-color: white; padding: 20px;"
|
|
||||||
id="qrcode"
|
|
||||||
></div>
|
|
||||||
</center>
|
|
||||||
</section>
|
|
||||||
<!-- /.content -->
|
|
||||||
</div>
|
</div>
|
||||||
<!-- /.content-wrapper -->
|
</div>
|
||||||
</div>
|
{% endblock %} {% block scripts %}
|
||||||
</body>
|
<script src="{{ url_for('static', filename='vendor/vue-qrcode@1.0.2/vue-qrcode.min.js') }}"></script>
|
||||||
<script>
|
<script>
|
||||||
new QRCode(document.getElementById('qrcode'), {
|
Vue.component(VueQrcode.name, VueQrcode)
|
||||||
text: '{{ticket}}',
|
new Vue({
|
||||||
width: 300,
|
el: '#vue',
|
||||||
height: 300,
|
mixins: [windowMixin]
|
||||||
colorDark: '#000000',
|
|
||||||
colorLight: '#ffffff',
|
|
||||||
correctLevel: QRCode.CorrectLevel.M
|
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
</html>
|
{% endblock %}
|
||||||
|
|
|
@ -1,174 +1,45 @@
|
||||||
import uuid
|
from flask import g, abort, render_template
|
||||||
|
from datetime import date, datetime
|
||||||
|
|
||||||
from flask import jsonify, render_template, request, redirect, url_for
|
from lnbits.decorators import check_user_exists, validate_uuids
|
||||||
from datetime import datetime
|
from http import HTTPStatus
|
||||||
|
|
||||||
from lnbits.db import open_db, open_ext_db
|
|
||||||
from lnbits.extensions.events import events_ext
|
from lnbits.extensions.events import events_ext
|
||||||
|
from .crud import get_ticket, get_event
|
||||||
|
|
||||||
|
|
||||||
@events_ext.route("/")
|
@events_ext.route("/")
|
||||||
|
@validate_uuids(["usr"], required=True)
|
||||||
|
@check_user_exists()
|
||||||
def index():
|
def index():
|
||||||
"""Main events link page."""
|
return render_template("events/index.html", user=g.user)
|
||||||
usr = request.args.get("usr")
|
|
||||||
|
|
||||||
if usr:
|
|
||||||
if not len(usr) > 20:
|
|
||||||
return redirect(url_for("home"))
|
|
||||||
|
|
||||||
# Get all the data
|
|
||||||
with open_db() as db:
|
|
||||||
user_wallets = db.fetchall("SELECT * FROM wallets WHERE user = ?", (usr,))
|
|
||||||
user_ext = db.fetchall("SELECT extension FROM extensions WHERE user = ? AND active = 1", (usr,))
|
|
||||||
user_ext = [v[0] for v in user_ext]
|
|
||||||
|
|
||||||
with open_ext_db("events") as events_ext_db:
|
|
||||||
user_ev = events_ext_db.fetchall("SELECT * FROM events WHERE usr = ?", (usr,))
|
|
||||||
|
|
||||||
# If del is selected by user from events page, the event link is to be deleted
|
|
||||||
evdel = request.args.get("del")
|
|
||||||
if evdel:
|
|
||||||
user_ev = events_ext_db.fetchall("SELECT * FROM events WHERE uni = ?", (evdel,))
|
|
||||||
events_ext_db.execute("DELETE FROM events WHERE uni = ?", (evdel,))
|
|
||||||
if user_ev[0][9] > 0:
|
|
||||||
events_ext_db.execute("DELETE FROM eventssold WHERE uni = ?", (user_ev[0][12],))
|
|
||||||
user_ev = events_ext_db.fetchall("SELECT * FROM events WHERE usr = ?", (usr,))
|
|
||||||
print(user_ext)
|
|
||||||
|
|
||||||
return render_template(
|
|
||||||
"events/index.html", user_wallets=user_wallets, user=usr, user_ext=user_ext, user_ev=user_ev
|
|
||||||
)
|
|
||||||
|
|
||||||
@events_ext.route("/create", methods=["GET", "POST"])
|
|
||||||
def create():
|
|
||||||
"""."""
|
|
||||||
|
|
||||||
data = request.json
|
|
||||||
tit = data["tit"]
|
|
||||||
wal = data["wal"]
|
|
||||||
cldate = data["cldate"]
|
|
||||||
notickets = data["notickets"]
|
|
||||||
prtick = data["prtickets"]
|
|
||||||
usr = data["usr"]
|
|
||||||
descr = data["descr"]
|
|
||||||
wall = wal.split("-")
|
|
||||||
|
|
||||||
# Form validation
|
|
||||||
if (
|
|
||||||
not tit.replace(" ", "").isalnum()
|
|
||||||
or wal == ""
|
|
||||||
or int(notickets) < 0
|
|
||||||
or int(prtick) < 0
|
|
||||||
):
|
|
||||||
return jsonify({"ERROR": "FORM ERROR"}), 401
|
|
||||||
|
|
||||||
# If id that means its a link being edited, delete the record first
|
|
||||||
if "id" in data:
|
|
||||||
unid = data["id"].split("-")
|
|
||||||
uni = unid[1]
|
|
||||||
unireg = unid[2]
|
|
||||||
with open_ext_db("events") as events_ext_db:
|
|
||||||
events_ext_db.execute("DELETE FROM events WHERE uni = ?", (unid[1],))
|
|
||||||
else:
|
|
||||||
uni = uuid.uuid4().hex
|
|
||||||
unireg = uuid.uuid4().hex
|
|
||||||
|
|
||||||
|
|
||||||
with open_db() as dbb:
|
@events_ext.route("/<event_id>")
|
||||||
user_wallets = dbb.fetchall("SELECT * FROM wallets WHERE user = ? AND id = ?", (usr, wall[1],))
|
def display(event_id):
|
||||||
if not user_wallets:
|
event = get_event(event_id) or abort(HTTPStatus.NOT_FOUND, "Event does not exist.")
|
||||||
return jsonify({"ERROR": "NO WALLET USER"}), 401
|
if event.amount_tickets < 1:
|
||||||
|
return render_template("events/error.html", event_name=event.name, event_error="Sorry, tickets are sold out :(")
|
||||||
|
datetime_object = datetime.strptime(event.closing_date, '%Y-%m-%d').date()
|
||||||
|
if date.today() > datetime_object:
|
||||||
|
return render_template("events/error.html", event_name=event.name, event_error="Sorry, ticket closing date has passed :(")
|
||||||
|
|
||||||
with open_db() as db:
|
|
||||||
user_ext = db.fetchall("SELECT * FROM extensions WHERE user = ?", (usr,))
|
|
||||||
user_ext = [v[0] for v in user_ext]
|
|
||||||
|
|
||||||
# Add to DB
|
return render_template("events/display.html", event_id=event_id, event_name=event.name, event_info=event.info, event_price=event.price_per_ticket)
|
||||||
with open_ext_db("events") as events_ext_db:
|
|
||||||
events_ext_db.execute(
|
|
||||||
"""
|
|
||||||
INSERT OR IGNORE INTO events
|
|
||||||
(usr, wal, walnme, walinvkey, uni, tit, cldate, notickets, prtick, descr, unireg)
|
|
||||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
||||||
""",
|
|
||||||
(
|
|
||||||
usr,
|
|
||||||
wall[1],
|
|
||||||
user_wallets[0][1],
|
|
||||||
user_wallets[0][4],
|
|
||||||
uni,
|
|
||||||
tit,
|
|
||||||
cldate,
|
|
||||||
notickets,
|
|
||||||
prtick,
|
|
||||||
descr,
|
|
||||||
unireg,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
user_ev = events_ext_db.fetchall("SELECT * FROM events WHERE usr = ?", (usr,))
|
|
||||||
|
|
||||||
if not user_ev:
|
@events_ext.route("/ticket/<ticket_id>")
|
||||||
return jsonify({"ERROR": "NO WALLET USER"}), 401
|
def ticket(ticket_id):
|
||||||
|
ticket = get_ticket(ticket_id) or abort(HTTPStatus.NOT_FOUND, "Ticket does not exist.")
|
||||||
|
event = get_event(ticket.event) or abort(HTTPStatus.NOT_FOUND, "Event does not exist.")
|
||||||
|
return render_template("events/ticket.html", ticket_id=ticket_id, ticket_name=event.name, ticket_info=event.info)
|
||||||
|
|
||||||
return render_template(
|
|
||||||
"events/index.html", user_wallets=user_wallets, user=usr, user_ext=user_ext, user_ev=user_ev
|
@events_ext.route("/register/<event_id>")
|
||||||
)
|
def register(event_id):
|
||||||
|
event = get_event(event_id) or abort(HTTPStatus.NOT_FOUND, "Event does not exist.")
|
||||||
|
|
||||||
|
return render_template("events/register.html", event_id=event_id, event_name=event.name, wallet_id=event.wallet)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@events_ext.route("/wave/<wave>/", methods=["GET", "POST"])
|
|
||||||
def wave(wave):
|
|
||||||
"""."""
|
|
||||||
|
|
||||||
with open_ext_db("events") as events_ext_db:
|
|
||||||
user_ev = events_ext_db.fetchall("SELECT * FROM events WHERE unireg = ?", (wave,))
|
|
||||||
if not user_ev:
|
|
||||||
return jsonify({"ERROR": "NO RECORD"}), 401
|
|
||||||
|
|
||||||
return render_template(
|
|
||||||
"events/display.html", wave=wave, nme=user_ev[0][6], descr=user_ev[0][11]
|
|
||||||
)
|
|
||||||
|
|
||||||
@events_ext.route("/registration/<wave>", methods=["GET", "POST"])
|
|
||||||
def registration(wave):
|
|
||||||
"""."""
|
|
||||||
with open_ext_db("events") as events_ext_db:
|
|
||||||
user_ev = events_ext_db.fetchall("SELECT * FROM events WHERE uni = ?", (wave,))
|
|
||||||
user_ev_sold = events_ext_db.fetchall("SELECT * FROM eventssold WHERE uni = ? AND paid = 1", (user_ev[0][12],))
|
|
||||||
if not user_ev:
|
|
||||||
return jsonify({"ERROR": "NO RECORD"}), 401
|
|
||||||
|
|
||||||
return render_template(
|
|
||||||
"events/registration.html", user_ev=user_ev, user_ev_sold=user_ev_sold
|
|
||||||
)
|
|
||||||
|
|
||||||
@events_ext.route("/ticket/", methods=["GET"])
|
|
||||||
def ticket():
|
|
||||||
"""."""
|
|
||||||
thehash = request.args.get("hash")
|
|
||||||
unireg = request.args.get("unireg")
|
|
||||||
|
|
||||||
#Double check the payment has cleared
|
|
||||||
with open_db() as db:
|
|
||||||
payment = db.fetchall("SELECT * FROM apipayments WHERE payhash = ?", (thehash,))
|
|
||||||
|
|
||||||
if not payment:
|
|
||||||
return jsonify({"status": "ERROR", "reason":"NO RECORD OF PAYMENT"}), 400
|
|
||||||
|
|
||||||
if payment[0][4] == 1:
|
|
||||||
return jsonify({"status": "ERROR", "reason":"NOT PAID"}), 400
|
|
||||||
|
|
||||||
#Update databases
|
|
||||||
with open_ext_db("events") as events_ext_db:
|
|
||||||
user_ev = events_ext_db.fetchall("SELECT * FROM events WHERE unireg = ?", (unireg,))
|
|
||||||
updatesold = user_ev[0][9] + 1
|
|
||||||
events_ext_db.execute("UPDATE events SET sold = ? WHERE unireg = ?", (updatesold, unireg,))
|
|
||||||
events_ext_db.execute("UPDATE eventssold SET paid = 1 WHERE hash = ?", (thehash,))
|
|
||||||
eventssold = events_ext_db.fetchall("SELECT * FROM eventssold WHERE hash = ?", (thehash,))
|
|
||||||
if not eventssold:
|
|
||||||
return jsonify({"status": "ERROR", "reason":"NO TICKET RECORD"}), 200
|
|
||||||
|
|
||||||
return render_template(
|
|
||||||
"events/ticket.html", name=eventssold[0][3], ticket=thehash
|
|
||||||
)
|
|
||||||
|
|
|
@ -1,65 +1,155 @@
|
||||||
import uuid
|
from flask import g, jsonify, request
|
||||||
import json
|
from http import HTTPStatus
|
||||||
import requests
|
|
||||||
|
|
||||||
from flask import jsonify, request, url_for
|
from lnbits.core.crud import get_user, get_wallet
|
||||||
from datetime import datetime
|
from lnbits.core.services import create_invoice
|
||||||
|
from lnbits.decorators import api_check_wallet_key, api_validate_post_request
|
||||||
|
from lnbits.settings import WALLET
|
||||||
|
|
||||||
from lnbits.db import open_db, open_ext_db
|
|
||||||
from lnbits.extensions.events import events_ext
|
from lnbits.extensions.events import events_ext
|
||||||
|
from .crud import create_ticket, get_ticket, get_tickets, delete_ticket, create_event, update_event, get_event, get_events, delete_event, get_event_tickets
|
||||||
@events_ext.route("/api/v1/getticket/", methods=["GET","POST"])
|
|
||||||
def api_getticket():
|
|
||||||
"""."""
|
|
||||||
|
|
||||||
data = request.json
|
|
||||||
unireg = data["unireg"]
|
|
||||||
name = data["name"]
|
|
||||||
email = request.args.get("ema")
|
|
||||||
|
|
||||||
with open_ext_db("events") as events_ext_db:
|
|
||||||
user_ev = events_ext_db.fetchall("SELECT * FROM events WHERE unireg = ?", (unireg,))
|
|
||||||
|
|
||||||
|
|
||||||
header = {"Content-Type": "application/json", "X-Api-Key": user_ev[0][4]}
|
#########Events##########
|
||||||
data = {"value": str(user_ev[0][10]), "memo": user_ev[0][6]}
|
|
||||||
print(url_for("api_invoices", _external=True))
|
|
||||||
r = requests.post(url=url_for("api_invoices", _external=True), headers=header, data=json.dumps(data))
|
|
||||||
r_json = r.json()
|
|
||||||
|
|
||||||
if "ERROR" in r_json:
|
|
||||||
return jsonify({"status": "ERROR", "reason": r_json["ERROR"]}), 400
|
|
||||||
|
|
||||||
events_ext_db.execute(
|
@events_ext.route("/api/v1/events", methods=["GET"])
|
||||||
"""
|
@api_check_wallet_key("invoice")
|
||||||
INSERT OR IGNORE INTO eventssold
|
def api_events():
|
||||||
(uni, email, name, hash)
|
wallet_ids = [g.wallet.id]
|
||||||
VALUES (?, ?, ?, ?)
|
|
||||||
""",
|
if "all_wallets" in request.args:
|
||||||
(
|
wallet_ids = get_user(g.wallet.user).wallet_ids
|
||||||
unireg,
|
|
||||||
email,
|
return jsonify([event._asdict() for event in get_events(wallet_ids)]), HTTPStatus.OK
|
||||||
name,
|
|
||||||
r_json["payment_hash"]
|
|
||||||
),
|
@events_ext.route("/api/v1/events", methods=["POST"])
|
||||||
|
@events_ext.route("/api/v1/events/<event_id>", methods=["PUT"])
|
||||||
|
@api_check_wallet_key("invoice")
|
||||||
|
@api_validate_post_request(
|
||||||
|
schema={
|
||||||
|
"wallet": {"type": "string", "empty": False, "required": True},
|
||||||
|
"name": {"type": "string", "empty": False, "required": True},
|
||||||
|
"info": {"type": "string", "min": 0, "required": True},
|
||||||
|
"closing_date": {"type": "string", "empty": False, "required": True},
|
||||||
|
"event_start_date": {"type": "string", "empty": False, "required": True},
|
||||||
|
"event_end_date": {"type": "string", "empty": False, "required": True},
|
||||||
|
"amount_tickets": {"type": "integer", "min": 0, "required": True},
|
||||||
|
"price_per_ticket": {"type": "integer", "min": 0, "required": True}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
def api_event_create(event_id=None):
|
||||||
|
if event_id:
|
||||||
|
event = get_event(event_id)
|
||||||
|
print(g.data)
|
||||||
|
|
||||||
|
if not event:
|
||||||
|
return jsonify({"message": "Form does not exist."}), HTTPStatus.NOT_FOUND
|
||||||
|
|
||||||
|
if event.wallet != g.wallet.id:
|
||||||
|
return jsonify({"message": "Not your event."}), HTTPStatus.FORBIDDEN
|
||||||
|
|
||||||
|
event = update_event(event_id, **g.data)
|
||||||
|
else:
|
||||||
|
event = create_event(**g.data)
|
||||||
|
print(event)
|
||||||
|
return jsonify(event._asdict()), HTTPStatus.CREATED
|
||||||
|
|
||||||
|
|
||||||
|
@events_ext.route("/api/v1/events/<event_id>", methods=["DELETE"])
|
||||||
|
@api_check_wallet_key("invoice")
|
||||||
|
def api_form_delete(event_id):
|
||||||
|
event = get_event(event_id)
|
||||||
|
|
||||||
|
if not event:
|
||||||
|
return jsonify({"message": "Event does not exist."}), HTTPStatus.NOT_FOUND
|
||||||
|
|
||||||
|
if event.wallet != g.wallet.id:
|
||||||
|
return jsonify({"message": "Not your event."}), HTTPStatus.FORBIDDEN
|
||||||
|
|
||||||
|
delete_event(event_id)
|
||||||
|
|
||||||
|
return "", HTTPStatus.NO_CONTENT
|
||||||
|
|
||||||
|
|
||||||
|
#########Tickets##########
|
||||||
|
|
||||||
|
@events_ext.route("/api/v1/tickets", methods=["GET"])
|
||||||
|
@api_check_wallet_key("invoice")
|
||||||
|
def api_tickets():
|
||||||
|
wallet_ids = [g.wallet.id]
|
||||||
|
|
||||||
|
if "all_wallets" in request.args:
|
||||||
|
wallet_ids = get_user(g.wallet.user).wallet_ids
|
||||||
|
|
||||||
|
return jsonify([ticket._asdict() for ticket in get_tickets(wallet_ids)]), HTTPStatus.OK
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@events_ext.route("/api/v1/tickets/<event_id>/<sats>", methods=["GET"])
|
||||||
|
def api_ticket_create(event_id, sats):
|
||||||
|
event = get_event(event_id)
|
||||||
|
|
||||||
|
try:
|
||||||
|
checking_id, payment_request = create_invoice(
|
||||||
|
wallet_id=event.wallet, amount=int(sats), memo=f"#lnticket {event_id}"
|
||||||
)
|
)
|
||||||
|
except Exception as e:
|
||||||
|
return jsonify({"message": str(e)}), HTTPStatus.INTERNAL_SERVER_ERROR
|
||||||
|
|
||||||
return jsonify({"status": "TRUE", "pay_req": r_json["pay_req"], "payment_hash": r_json["payment_hash"]}), 200
|
return jsonify({"checking_id": checking_id, "payment_request": payment_request}), HTTPStatus.OK
|
||||||
|
|
||||||
|
|
||||||
@events_ext.route("/api/v1/checkticket/", methods=["GET"])
|
|
||||||
def api_checkticket():
|
|
||||||
"""."""
|
|
||||||
thehash = request.args.get("thehash")
|
|
||||||
#Check databases
|
|
||||||
with open_ext_db("events") as events_ext_db:
|
|
||||||
eventssold = events_ext_db.fetchall("SELECT * FROM eventssold WHERE hash = ?", (thehash,))
|
|
||||||
if not eventssold:
|
|
||||||
return jsonify({"status": "ERROR", "reason":"NO TICKET RECORD"}), 200
|
|
||||||
if eventssold[0][4] == 0:
|
|
||||||
return jsonify({"status": "ERROR", "reason":"NOT PAID"}), 200
|
|
||||||
|
|
||||||
with open_ext_db("events") as events_ext_db:
|
@events_ext.route("/api/v1/tickets/<checking_id>", methods=["POST"])
|
||||||
events_ext_db.execute("UPDATE eventssold SET reg = 1 WHERE hash = ?", (thehash,))
|
@api_validate_post_request(
|
||||||
|
schema={
|
||||||
|
"event": {"type": "string", "empty": False, "required": True},
|
||||||
|
"name": {"type": "string", "empty": False, "required": True},
|
||||||
|
"email": {"type": "string", "empty": False, "required": True}
|
||||||
|
})
|
||||||
|
def api_ticket_send_ticket(checking_id):
|
||||||
|
|
||||||
|
event = get_event(g.data['event'])
|
||||||
|
if not event:
|
||||||
|
return jsonify({"message": "LNTicket does not exist."}), HTTPStatus.NOT_FOUND
|
||||||
|
try:
|
||||||
|
is_paid = not WALLET.get_invoice_status(checking_id).pending
|
||||||
|
except Exception:
|
||||||
|
return jsonify({"message": "Not paid."}), HTTPStatus.NOT_FOUND
|
||||||
|
|
||||||
|
if is_paid:
|
||||||
|
wallet = get_wallet(event.wallet)
|
||||||
|
payment = wallet.get_payment(checking_id)
|
||||||
|
payment.set_pending(False)
|
||||||
|
ticket = create_ticket(wallet=event.wallet, **g.data)
|
||||||
|
|
||||||
|
return jsonify({"paid": True, "ticket_id": ticket.id}), HTTPStatus.OK
|
||||||
|
|
||||||
|
return jsonify({"paid": False}), HTTPStatus.OK
|
||||||
|
|
||||||
|
|
||||||
|
@events_ext.route("/api/v1/tickets/<ticket_id>", methods=["DELETE"])
|
||||||
|
@api_check_wallet_key("invoice")
|
||||||
|
def api_ticket_delete(ticket_id):
|
||||||
|
ticket = get_ticket(ticket_id)
|
||||||
|
|
||||||
|
if not ticket:
|
||||||
|
return jsonify({"message": "Ticket does not exist."}), HTTPStatus.NOT_FOUND
|
||||||
|
|
||||||
|
if ticket.wallet != g.wallet.id:
|
||||||
|
return jsonify({"message": "Not your ticket."}), HTTPStatus.FORBIDDEN
|
||||||
|
|
||||||
|
delete_ticket(ticket_id)
|
||||||
|
|
||||||
|
return "", HTTPStatus.NO_CONTENT
|
||||||
|
|
||||||
|
|
||||||
|
#########EventTickets##########
|
||||||
|
|
||||||
|
@events_ext.route("/api/v1/eventtickets/<wallet_id>/<event_id>", methods=["GET"])
|
||||||
|
def api_event_tickets(wallet_id, event_id):
|
||||||
|
|
||||||
|
return jsonify([ticket._asdict() for ticket in get_event_tickets(wallet_id=wallet_id, event_id=event_id)]), HTTPStatus.OK
|
||||||
|
|
||||||
return jsonify({"status": "TRUE", "name": eventssold[0][3]}), 200
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user