add customer stall component, remove toolbar
This commit is contained in:
parent
4fa136164a
commit
800619df9f
3 changed files with 249 additions and 15 deletions
102
static/components/customer-stall/customer-stall.html
Normal file
102
static/components/customer-stall/customer-stall.html
Normal file
|
|
@ -0,0 +1,102 @@
|
||||||
|
<div>
|
||||||
|
<q-toolbar>
|
||||||
|
<q-breadcrumbs class="cursor">
|
||||||
|
<q-breadcrumbs-el
|
||||||
|
label="Market"
|
||||||
|
icon="home"
|
||||||
|
@click="$emit('change-page', 'market')"
|
||||||
|
style="cursor: pointer"
|
||||||
|
></q-breadcrumbs-el>
|
||||||
|
<q-breadcrumbs-el :label="stall.name" icon="widgets"></q-breadcrumbs-el>
|
||||||
|
</q-breadcrumbs>
|
||||||
|
<q-toolbar-title></q-toolbar-title>
|
||||||
|
<q-btn dense round flat icon="shopping_cart" class="q-ml-md"></q-btn>
|
||||||
|
</q-toolbar>
|
||||||
|
<div class="row q-col-gutter-md">
|
||||||
|
<div
|
||||||
|
class="col-xs-12 col-sm-6 col-md-4 col-lg-3"
|
||||||
|
v-for="(item, idx) in products"
|
||||||
|
:key="idx"
|
||||||
|
>
|
||||||
|
<q-card class="card--product">
|
||||||
|
<q-img
|
||||||
|
:src="item.image ? item.image : '/nostrmarket/static/images/placeholder.png'"
|
||||||
|
alt="Product Image"
|
||||||
|
loading="lazy"
|
||||||
|
spinner-color="white"
|
||||||
|
fit="contain"
|
||||||
|
height="300px"
|
||||||
|
></q-img>
|
||||||
|
|
||||||
|
<q-card-section class="q-pb-xs q-pt-md">
|
||||||
|
<q-btn
|
||||||
|
round
|
||||||
|
:disabled="item.amount < 1"
|
||||||
|
color="primary"
|
||||||
|
icon="shopping_cart"
|
||||||
|
size="lg"
|
||||||
|
style="
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
"
|
||||||
|
@click=""
|
||||||
|
><q-tooltip> Add to cart </q-tooltip></q-btn
|
||||||
|
>
|
||||||
|
<div class="row no-wrap items-center">
|
||||||
|
<div class="col text-subtitle2 ellipsis-2-lines">
|
||||||
|
{{ item.name }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- <q-rating v-model="stars" color="orange" :max="5" readonly size="17px"></q-rating> -->
|
||||||
|
</q-card-section>
|
||||||
|
|
||||||
|
<q-card-section class="q-py-sm">
|
||||||
|
<div>
|
||||||
|
<div class="text-caption text-weight-bolder">
|
||||||
|
{{ item.stallName }}
|
||||||
|
</div>
|
||||||
|
<span v-if="item.currency == 'sat'">
|
||||||
|
<span class="text-h6">{{ item.price }} sats</span
|
||||||
|
><span class="q-ml-sm text-grey-6"
|
||||||
|
>BTC {{ (item.price / 1e8).toFixed(8) }}</span
|
||||||
|
>
|
||||||
|
</span>
|
||||||
|
<span v-else>
|
||||||
|
<span class="text-h6">{{ item.formatedPrice }}</span>
|
||||||
|
<span v-if="exchangeRates" class="q-ml-sm text-grey-6"
|
||||||
|
>({{ item.priceInSats }} sats)</span
|
||||||
|
>
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
class="q-ml-md text-caption text-green-8 text-weight-bolder q-mt-md"
|
||||||
|
>{{ item.amount }} left</span
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
<div v-if="item.categories" class="text-subtitle1">
|
||||||
|
<q-chip v-for="(cat, i) in item.categories" :key="i" dense
|
||||||
|
>{{cat}}</q-chip
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="text-caption text-grey ellipsis-2-lines"
|
||||||
|
style="min-height: 40px"
|
||||||
|
>
|
||||||
|
<p v-if="item.description">{{ item.description }}</p>
|
||||||
|
</div>
|
||||||
|
</q-card-section>
|
||||||
|
|
||||||
|
<q-separator></q-separator>
|
||||||
|
|
||||||
|
<q-card-actions>
|
||||||
|
<span>Stall: {{ item.stallName }}</span>
|
||||||
|
<div class="q-ml-auto">
|
||||||
|
<q-btn flat dense>See product</q-btn>
|
||||||
|
</div>
|
||||||
|
</q-card-actions>
|
||||||
|
</q-card>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
17
static/components/customer-stall/customer-stall.js
Normal file
17
static/components/customer-stall/customer-stall.js
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
async function customerStall(path) {
|
||||||
|
const template = await loadTemplateAsync(path)
|
||||||
|
Vue.component('customer-stall', {
|
||||||
|
name: 'customer-stall',
|
||||||
|
template,
|
||||||
|
|
||||||
|
props: ['stall', 'products', 'exchange-rates'],
|
||||||
|
data: function () {
|
||||||
|
return {}
|
||||||
|
},
|
||||||
|
methods: {},
|
||||||
|
created() {
|
||||||
|
console.log(this.stall)
|
||||||
|
console.log(this.products)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
@ -137,12 +137,50 @@
|
||||||
</template>
|
</template>
|
||||||
</q-input>
|
</q-input>
|
||||||
</q-toolbar>
|
</q-toolbar>
|
||||||
|
<!-- <q-toolbar>
|
||||||
|
<q-breadcrumbs class="cursor">
|
||||||
|
<q-breadcrumbs-el
|
||||||
|
label="Market"
|
||||||
|
icon="home"
|
||||||
|
@click="navigateTo('market')"
|
||||||
|
></q-breadcrumbs-el>
|
||||||
|
<q-breadcrumbs-el
|
||||||
|
v-if="activeStall"
|
||||||
|
@click="activeProduct && navigateTo('stall', activeStall)"
|
||||||
|
:label="stallName"
|
||||||
|
icon="widgets"
|
||||||
|
></q-breadcrumbs-el>
|
||||||
|
<q-breadcrumbs-el
|
||||||
|
v-if="activeProduct"
|
||||||
|
:label="productName"
|
||||||
|
icon="navigation"
|
||||||
|
></q-breadcrumbs-el>
|
||||||
|
</q-breadcrumbs>
|
||||||
|
<q-toolbar-title></q-toolbar-title>
|
||||||
|
<q-btn
|
||||||
|
v-if="activeStall"
|
||||||
|
dense
|
||||||
|
round
|
||||||
|
flat
|
||||||
|
icon="shopping_cart"
|
||||||
|
class="q-ml-md"
|
||||||
|
></q-btn>
|
||||||
|
</q-toolbar> -->
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<customer-market
|
|
||||||
v-if="activePage == 'market'"
|
<customer-stall
|
||||||
|
v-if="activeStall"
|
||||||
|
:stall="stalls.find(stall => stall.id == activeStall)"
|
||||||
:products="filterProducts"
|
:products="filterProducts"
|
||||||
:exchange-rates="exchangeRates"
|
:exchange-rates="exchangeRates"
|
||||||
|
@change-page="navigateTo"
|
||||||
|
></customer-stall>
|
||||||
|
<customer-market
|
||||||
|
v-else
|
||||||
|
:products="filterProducts"
|
||||||
|
:exchange-rates="exchangeRates"
|
||||||
|
@change-page="navigateTo"
|
||||||
></customer-market>
|
></customer-market>
|
||||||
</q-page-container>
|
</q-page-container>
|
||||||
</q-layout>
|
</q-layout>
|
||||||
|
|
@ -151,6 +189,7 @@
|
||||||
|
|
||||||
<script src="{{ url_for('nostrmarket_static', path='js/utils.js') }}"></script>
|
<script src="{{ url_for('nostrmarket_static', path='js/utils.js') }}"></script>
|
||||||
<script src="{{ url_for('nostrmarket_static', path='components/customer-market/customer-market.js') }}"></script>
|
<script src="{{ url_for('nostrmarket_static', path='components/customer-market/customer-market.js') }}"></script>
|
||||||
|
<script src="{{ url_for('nostrmarket_static', path='components/customer-stall/customer-stall.js') }}"></script>
|
||||||
<script>
|
<script>
|
||||||
const nostr = window.NostrTools
|
const nostr = window.NostrTools
|
||||||
const defaultRelays = [
|
const defaultRelays = [
|
||||||
|
|
@ -171,14 +210,18 @@
|
||||||
event.content = JSON.parse(event.content)
|
event.content = JSON.parse(event.content)
|
||||||
return {
|
return {
|
||||||
...event,
|
...event,
|
||||||
...Object.fromEntries(event.tags)
|
...Object.values(event.tags).reduce((acc, tag) => {
|
||||||
|
let [key, value] = tag
|
||||||
|
return {...acc, [key]: [...(acc[key] || []), value]}
|
||||||
|
}, {})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Vue.component(VueQrcode.name, VueQrcode)
|
Vue.component(VueQrcode.name, VueQrcode)
|
||||||
|
|
||||||
Promise.all([
|
Promise.all([
|
||||||
customerMarket('static/components/customer-market/customer-market.html')
|
customerMarket('static/components/customer-market/customer-market.html'),
|
||||||
|
customerStall('static/components/customer-stall/customer-stall.html')
|
||||||
])
|
])
|
||||||
|
|
||||||
new Vue({
|
new Vue({
|
||||||
|
|
@ -190,26 +233,40 @@
|
||||||
pubkeys: new Set(),
|
pubkeys: new Set(),
|
||||||
relays: new Set(),
|
relays: new Set(),
|
||||||
events: [],
|
events: [],
|
||||||
stalls: new Map(),
|
stalls: [],
|
||||||
products: [],
|
products: [],
|
||||||
profiles: new Map(),
|
profiles: new Map(),
|
||||||
searchText: null,
|
searchText: null,
|
||||||
exchangeRates: null,
|
exchangeRates: null,
|
||||||
inputPubkey: null,
|
inputPubkey: null,
|
||||||
inputRelay: null,
|
inputRelay: null,
|
||||||
activePage: 'market'
|
activePage: 'market',
|
||||||
|
activeStall: null,
|
||||||
|
activeProduct: null
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
filterProducts() {
|
filterProducts() {
|
||||||
if (!this.searchText || this.searchText.length < 2) return this.products
|
let products = this.products
|
||||||
return this.products.filter(p => {
|
if (this.activeStall) {
|
||||||
|
products = products.filter(p => p.stall == this.activeStall)
|
||||||
|
}
|
||||||
|
if (!this.searchText || this.searchText.length < 2) return products
|
||||||
|
return products.filter(p => {
|
||||||
return (
|
return (
|
||||||
p.product.includes(this.searchText) ||
|
p.name.includes(this.searchText) ||
|
||||||
p.description.includes(this.searchText) ||
|
p.description.includes(this.searchText) ||
|
||||||
p.categories.includes(this.searchText)
|
p.categories.includes(this.searchText)
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
},
|
||||||
|
stallName() {
|
||||||
|
return this.stalls.find(s => s.id == this.activeStall)?.name || 'Stall'
|
||||||
|
},
|
||||||
|
productName() {
|
||||||
|
return (
|
||||||
|
this.products.find(p => p.id == this.activeProduct)?.name || 'Product'
|
||||||
|
)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async created() {
|
async created() {
|
||||||
|
|
@ -220,19 +277,38 @@
|
||||||
this.pubkeys.add(
|
this.pubkeys.add(
|
||||||
'8f69ac99b96f7c4ad58b98cc38fe5d35ce02daefae7d1609c797ce3b4f92f5fd'
|
'8f69ac99b96f7c4ad58b98cc38fe5d35ce02daefae7d1609c797ce3b4f92f5fd'
|
||||||
)
|
)
|
||||||
|
// stall ids S4hQgtTwiF5kGJZPbqMH9M jkCbdtkXeMjGBY3LBf8yn4
|
||||||
|
this.$q.loading.show()
|
||||||
|
this.relays = new Set(defaultRelays)
|
||||||
|
// Get notes from Nostr
|
||||||
|
await this.initNostr()
|
||||||
|
|
||||||
// What component to render on start
|
// What component to render on start
|
||||||
|
let merchant_pubkey = JSON.parse('{{ merchant_pubkey | tojson }}')
|
||||||
let stall_id = JSON.parse('{{ stall_id | tojson }}')
|
let stall_id = JSON.parse('{{ stall_id | tojson }}')
|
||||||
let product_id = JSON.parse('{{ product_id | tojson }}')
|
let product_id = JSON.parse('{{ product_id | tojson }}')
|
||||||
|
if (merchant_pubkey) {
|
||||||
|
this.pubkeys.add(merchant_pubkey)
|
||||||
|
/*LNbits.utils
|
||||||
|
.confirmDialog(
|
||||||
|
`We found a merchant pubkey in your request. Do you want to add it to the merchants list?`
|
||||||
|
)
|
||||||
|
.onCancel(() => {})
|
||||||
|
.onDismiss(() => {})
|
||||||
|
.onOk(() => {
|
||||||
|
this.pubkeys.add(merchant_pubkey)
|
||||||
|
})*/
|
||||||
|
}
|
||||||
if (stall_id) {
|
if (stall_id) {
|
||||||
if (product_id) {
|
if (product_id) {
|
||||||
this.activePage = 'product'
|
this.activePage = 'product'
|
||||||
|
this.activeProduct = product_id
|
||||||
} else {
|
} else {
|
||||||
this.activePage = 'stall'
|
this.activePage = 'stall'
|
||||||
|
this.activeStall = stall_id
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.$q.loading.show()
|
|
||||||
this.relays = new Set(defaultRelays)
|
|
||||||
await this.initNostr()
|
|
||||||
this.$q.loading.hide()
|
this.$q.loading.hide()
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
|
@ -245,11 +321,12 @@
|
||||||
let sub = await pool
|
let sub = await pool
|
||||||
.list(relays, [
|
.list(relays, [
|
||||||
{
|
{
|
||||||
kinds: [0, 30005],
|
kinds: [0, 30005], // for production kind is 30017
|
||||||
authors: Array.from(this.pubkeys)
|
authors: Array.from(this.pubkeys)
|
||||||
}
|
}
|
||||||
])
|
])
|
||||||
.then(events => {
|
.then(events => {
|
||||||
|
console.log(events)
|
||||||
this.events = events || []
|
this.events = events || []
|
||||||
this.events.map(eventToObj).map(e => {
|
this.events.map(eventToObj).map(e => {
|
||||||
if (e.kind == 0) {
|
if (e.kind == 0) {
|
||||||
|
|
@ -257,10 +334,10 @@
|
||||||
return
|
return
|
||||||
} else if (e.content.stall) {
|
} else if (e.content.stall) {
|
||||||
//it's a product `d` is the prod. id
|
//it's a product `d` is the prod. id
|
||||||
products.set(e.d, {...e.content, id: e.d})
|
products.set(e.d, {...e.content, id: e.d, categories: e.t})
|
||||||
} else {
|
} else {
|
||||||
// it's a stall `d` is the stall id
|
// it's a stall `d` is the stall id
|
||||||
stalls.set(e.d, {...e.content, id: e.d})
|
stalls.set(e.d, {...e.content, id: e.d, pubkey: e.pubkey})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
@ -291,6 +368,44 @@
|
||||||
LNbits.utils.notifyApiError(error)
|
LNbits.utils.notifyApiError(error)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
navigateTo(page, opts = {stall: null, product: null, pubkey: null}) {
|
||||||
|
let {stall, product, pubkey} = opts
|
||||||
|
let url = new URL(window.location)
|
||||||
|
|
||||||
|
if (pubkey) url.searchParams.set('merchant_pubkey', pubkey)
|
||||||
|
if (stall && !pubkey) {
|
||||||
|
pubkey = this.stalls.find(s => s.id == stall).pubkey
|
||||||
|
url.searchParams.set('merchant_pubkey', pubkey)
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (page) {
|
||||||
|
case 'stall':
|
||||||
|
if (stall) {
|
||||||
|
this.activeStall = stall
|
||||||
|
url.searchParams.set('stall_id', stall)
|
||||||
|
}
|
||||||
|
break
|
||||||
|
case 'product':
|
||||||
|
if (stall && product) {
|
||||||
|
this.activeStall = stall
|
||||||
|
this.activeProduct = product
|
||||||
|
url.searchParams.set('stall_id', stall)
|
||||||
|
url.searchParams.set('product_id', product)
|
||||||
|
}
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
this.activeStall = null
|
||||||
|
this.activeProduct = null
|
||||||
|
url.searchParams.delete('merchant_pubkey')
|
||||||
|
url.searchParams.delete('stall_id')
|
||||||
|
url.searchParams.delete('product_id')
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
window.history.pushState({}, '', url)
|
||||||
|
this.activePage = page
|
||||||
|
},
|
||||||
|
|
||||||
getValueInSats(amount, unit = 'USD') {
|
getValueInSats(amount, unit = 'USD') {
|
||||||
if (!this.exchangeRates) return 0
|
if (!this.exchangeRates) return 0
|
||||||
return Math.ceil(
|
return Math.ceil(
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue