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>
|
||||
</q-input>
|
||||
</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>
|
||||
<customer-market
|
||||
v-if="activePage == 'market'"
|
||||
|
||||
<customer-stall
|
||||
v-if="activeStall"
|
||||
:stall="stalls.find(stall => stall.id == activeStall)"
|
||||
:products="filterProducts"
|
||||
:exchange-rates="exchangeRates"
|
||||
@change-page="navigateTo"
|
||||
></customer-stall>
|
||||
<customer-market
|
||||
v-else
|
||||
:products="filterProducts"
|
||||
:exchange-rates="exchangeRates"
|
||||
@change-page="navigateTo"
|
||||
></customer-market>
|
||||
</q-page-container>
|
||||
</q-layout>
|
||||
|
|
@ -151,6 +189,7 @@
|
|||
|
||||
<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-stall/customer-stall.js') }}"></script>
|
||||
<script>
|
||||
const nostr = window.NostrTools
|
||||
const defaultRelays = [
|
||||
|
|
@ -171,14 +210,18 @@
|
|||
event.content = JSON.parse(event.content)
|
||||
return {
|
||||
...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)
|
||||
|
||||
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({
|
||||
|
|
@ -190,26 +233,40 @@
|
|||
pubkeys: new Set(),
|
||||
relays: new Set(),
|
||||
events: [],
|
||||
stalls: new Map(),
|
||||
stalls: [],
|
||||
products: [],
|
||||
profiles: new Map(),
|
||||
searchText: null,
|
||||
exchangeRates: null,
|
||||
inputPubkey: null,
|
||||
inputRelay: null,
|
||||
activePage: 'market'
|
||||
activePage: 'market',
|
||||
activeStall: null,
|
||||
activeProduct: null
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
filterProducts() {
|
||||
if (!this.searchText || this.searchText.length < 2) return this.products
|
||||
return this.products.filter(p => {
|
||||
let products = this.products
|
||||
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 (
|
||||
p.product.includes(this.searchText) ||
|
||||
p.name.includes(this.searchText) ||
|
||||
p.description.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() {
|
||||
|
|
@ -220,19 +277,38 @@
|
|||
this.pubkeys.add(
|
||||
'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
|
||||
let merchant_pubkey = JSON.parse('{{ merchant_pubkey | tojson }}')
|
||||
let stall_id = JSON.parse('{{ stall_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 (product_id) {
|
||||
this.activePage = 'product'
|
||||
this.activeProduct = product_id
|
||||
} else {
|
||||
this.activePage = 'stall'
|
||||
this.activeStall = stall_id
|
||||
}
|
||||
}
|
||||
this.$q.loading.show()
|
||||
this.relays = new Set(defaultRelays)
|
||||
await this.initNostr()
|
||||
|
||||
this.$q.loading.hide()
|
||||
},
|
||||
methods: {
|
||||
|
|
@ -245,11 +321,12 @@
|
|||
let sub = await pool
|
||||
.list(relays, [
|
||||
{
|
||||
kinds: [0, 30005],
|
||||
kinds: [0, 30005], // for production kind is 30017
|
||||
authors: Array.from(this.pubkeys)
|
||||
}
|
||||
])
|
||||
.then(events => {
|
||||
console.log(events)
|
||||
this.events = events || []
|
||||
this.events.map(eventToObj).map(e => {
|
||||
if (e.kind == 0) {
|
||||
|
|
@ -257,10 +334,10 @@
|
|||
return
|
||||
} else if (e.content.stall) {
|
||||
//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 {
|
||||
// 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
|
||||
}
|
||||
})
|
||||
|
|
@ -291,6 +368,44 @@
|
|||
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') {
|
||||
if (!this.exchangeRates) return 0
|
||||
return Math.ceil(
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue