routing done

This commit is contained in:
Tiago Vasconcelos 2023-03-03 16:53:35 +00:00
parent ed643f1c95
commit 8a2bc0e345
9 changed files with 78 additions and 267 deletions

View file

@ -10,7 +10,7 @@
v-for="(item, idx) in products" v-for="(item, idx) in products"
:key="idx" :key="idx"
> >
<product-card :product="item" @change-page="change-page"></product-card> <product-card :product="item" @change-page="changePageM"></product-card>
</div> </div>
</div> </div>
</div> </div>

View file

@ -8,7 +8,11 @@ async function customerMarket(path) {
data: function () { data: function () {
return {} return {}
}, },
methods: {}, methods: {
changePageM(page, opts) {
this.$emit('change-page', page, opts)
}
},
created() {} created() {}
}) })
} }

View file

@ -10,94 +10,25 @@
<q-breadcrumbs-el :label="stall.name" icon="widgets"></q-breadcrumbs-el> <q-breadcrumbs-el :label="stall.name" icon="widgets"></q-breadcrumbs-el>
</q-breadcrumbs> </q-breadcrumbs>
<q-toolbar-title></q-toolbar-title> <q-toolbar-title></q-toolbar-title>
<q-btn dense round flat icon="shopping_cart" class="q-ml-md"></q-btn> <shopping-cart></shopping-cart>
</q-toolbar> </q-toolbar>
<product-detail v-if="productDetail" :product="mock"></product-detail> <div class="row">
<product-detail
class="col-12"
v-if="productDetail && product"
:product="product"
></product-detail>
<div class="col-12 q-my-lg">
<q-separator></q-separator>
</div>
</div>
<div class="row q-col-gutter-md"> <div class="row q-col-gutter-md">
<div <div
class="col-xs-12 col-sm-6 col-md-4 col-lg-3" class="col-xs-12 col-sm-6 col-md-4 col-lg-3"
v-for="(item, idx) in products" v-for="(item, idx) in products"
:key="idx" :key="idx"
> >
<q-card class="card--product"> <product-card :product="item" @change-page="changePageS"></product-card>
<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> </div>
</div> </div>

View file

@ -1,34 +1,32 @@
async function customerStall(path) { async function customerStall(path) {
const template = await loadTemplateAsync(path) const template = await loadTemplateAsync(path)
const mock = {
stall: '4M8j9KKGzUckHgb4C3pKCv',
name: 'product 1',
description:
'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Leo integer malesuada nunc vel risus commodo. Sapien faucibus et molestie ac feugiat sed lectus vestibulum mattis. Cras ornare arcu dui vivamus. Risus pretium quam vulputate dignissim suspendisse in est ante in. Faucibus in ornare quam viverra orci sagittis eu volutpat odio.',
amount: 100,
price: '10',
images: ['https://i.imgur.com/cEfpEjq.jpeg'],
id: ['RyMbE9Hdwk9X333JKtkkNS'],
categories: ['crafts', 'robots'],
currency: 'EUR',
stallName: 'stall 1',
formatedPrice: '€10.00',
priceInSats: 0
}
Vue.component('customer-stall', { Vue.component('customer-stall', {
name: 'customer-stall', name: 'customer-stall',
template, template,
props: ['stall', 'products', 'exchange-rates', 'product-detail'], props: [
'stall',
'products',
'exchange-rates',
'product-detail',
'change-page'
],
data: function () { data: function () {
return { return {}
mock: mock },
computed: {
product() {
if (this.productDetail) {
return this.products.find(p => p.id == this.productDetail)
}
} }
}, },
methods: {}, methods: {
created() { changePageS(page, opts) {
console.log(this.stall) this.$emit('change-page', page, opts)
console.log(this.products) }
} },
created() {}
}) })
} }

View file

@ -52,6 +52,7 @@
<q-card-actions> <q-card-actions>
<span>Stall: {{ product.stallName }}</span> <span>Stall: {{ product.stallName }}</span>
<span>{{ $parent.activeStall }}</span>
<div class="q-ml-auto"> <div class="q-ml-auto">
<q-btn <q-btn
flat flat

View file

@ -4,7 +4,7 @@ async function productCard(path) {
name: 'product-card', name: 'product-card',
template, template,
props: ['product'], props: ['product', 'change-page'],
data: function () { data: function () {
return {} return {}
}, },

View file

@ -2,6 +2,7 @@
<div class="col-lg-5 col-md-5 col-sm-12 col-xs-12"> <div class="col-lg-5 col-md-5 col-sm-12 col-xs-12">
<div class="q-pa-md"> <div class="q-pa-md">
<q-carousel <q-carousel
v-if="product.images"
swipeable swipeable
animated animated
v-model="slide" v-model="slide"
@ -48,19 +49,6 @@
class="q-ml-md text-caption text-green-8 text-weight-bolder q-mt-md" class="q-ml-md text-caption text-green-8 text-weight-bolder q-mt-md"
>{{ product.amount > 0 ? 'In stock.' : 'Out of stock.' }}</span >{{ product.amount > 0 ? 'In stock.' : 'Out of stock.' }}</span
> >
<!-- <div class="text-caption text-weight-bolder q-mt-sm">
Special Price
</div>
<span class="text-h6">₹3,149</span
><span
class="q-ml-sm text-grey-6"
style="text-decoration: line-through"
>₹3,699</span
>
<span
class="q-ml-md text-caption text-green-8 text-weight-bolder q-mt-md"
>20% off</span
> -->
</div> </div>
<div class="q-mt-md"> <div class="q-mt-md">
<q-btn <q-btn
@ -78,110 +66,6 @@
</div> </div>
</div> </div>
<!-- RATING TO BE DONE --> <!-- RATING TO BE DONE -->
<div
class="col-lg-5 col-md-5 col-sm-12 col-xs-12 q-mt-md q-pt-xs q-pl-lg"
>
<div class="text-subtitle2">Customer rating</div>
<div class="text-h3">4.2</div>
<div>
<q-rating
v-model="rating_point"
max="5"
size="2em"
color="orange"
icon="star_border"
icon-selected="star"
icon-half="star_half"
no-dimming
readonly
/>
</div>
<div class="text-subtitle2 text-grey-8">(357 reviews)</div>
<div class="text-subtitle2 text-grey-10 q-mt-sm">
93% would recommend to a friend
</div>
<q-list dense bordered padding class="no-border q-mt-lg q-pr-xl">
<q-item style="padding-left: 0 !important" v-ripple>
<span class="text-subtitle2 q-mr-xs">5</span>
<q-icon name="star" size="1.5em" color="orange"></q-icon>
<q-linear-progress
class="q-ml-sm q-mr-sm"
style="margin-top: 5px"
size="13px"
:value="0.9"
/>
<span
style="margin-top: 2px"
class="text-caption text-weight-bold text-grey-8"
>273</span
>
</q-item>
<q-item style="padding-left: 0 !important" v-ripple>
<span class="text-subtitle2 q-mr-xs">4</span>
<q-icon name="star" size="1.5em" color="orange"></q-icon>
<q-linear-progress
class="q-ml-sm q-mr-sm"
style="margin-top: 5px"
size="13px"
:value="0.6"
/>
<span
style="margin-top: 2px"
class="text-caption text-weight-bold text-grey-8"
>&nbsp;&nbsp;69</span
>
</q-item>
<q-item style="padding-left: 0 !important" v-ripple>
<span class="text-subtitle2 q-mr-xs">3</span>
<q-icon name="star" size="1.5em" color="orange"></q-icon>
<q-linear-progress
class="q-ml-sm q-mr-sm"
style="margin-top: 5px"
size="13px"
:value="0.1"
/>
<span
style="margin-top: 2px"
class="text-caption text-weight-bold text-grey-8"
>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;6</span
>
</q-item>
<q-item style="padding-left: 0 !important" v-ripple>
<span class="text-subtitle2 q-mr-xs">2</span>
<q-icon name="star" size="1.5em" color="orange"></q-icon>
<q-linear-progress
class="q-ml-sm q-mr-sm"
style="margin-top: 5px"
size="13px"
:value="0.1"
/>
<span
style="margin-top: 2px"
class="text-caption text-weight-bold text-grey-8"
>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;3</span
>
</q-item>
<q-item style="padding-left: 0 !important" v-ripple>
<span class="text-subtitle2 q-mr-xs">1</span>
<q-icon name="star" size="1.5em" color="orange"></q-icon>
<q-linear-progress
class="q-ml-sm q-mr-sm"
style="margin-top: 5px"
size="13px"
:value="0.1"
/>
<span
style="margin-top: 2px"
class="text-caption text-weight-bold text-grey-8"
>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;6</span
>
</q-item>
</q-list>
</div>
</div> </div>
</div> </div>
<div class="col-12 q-mb-lg">
<q-separator></q-separator>
</div>
</div> </div>

View file

@ -30,7 +30,8 @@ const market = async () => {
productCard('static/components/product-card/product-card.html'), productCard('static/components/product-card/product-card.html'),
customerMarket('static/components/customer-market/customer-market.html'), customerMarket('static/components/customer-market/customer-market.html'),
customerStall('static/components/customer-stall/customer-stall.html'), customerStall('static/components/customer-stall/customer-stall.html'),
productDetail('static/components/product-detail/product-detail.html') productDetail('static/components/product-detail/product-detail.html'),
shoppingCart('static/components/shopping-cart/shopping-cart.html')
]) ])
new Vue({ new Vue({
@ -58,7 +59,7 @@ const market = async () => {
filterProducts() { filterProducts() {
let products = this.products let products = this.products
if (this.activeStall) { if (this.activeStall) {
products = products.filter(p => p.stall == this.activeStall) products = products.filter(p => p.stall_id == this.activeStall)
} }
if (!this.searchText || this.searchText.length < 2) return products if (!this.searchText || this.searchText.length < 2) return products
return products.filter(p => { return products.filter(p => {
@ -76,6 +77,9 @@ const market = async () => {
return ( return (
this.products.find(p => p.id == this.activeProduct)?.name || 'Product' this.products.find(p => p.id == this.activeProduct)?.name || 'Product'
) )
},
isLoading() {
return this.$q.loading.isActive
} }
}, },
async created() { async created() {
@ -88,51 +92,17 @@ const market = async () => {
} }
if (relays && relays.length) { if (relays && relays.length) {
this.relays = new Set([...defaultRelays, ...relays]) this.relays = new Set([...defaultRelays, ...relays])
} else {
this.relays = new Set(defaultRelays)
} }
} catch (e) { } catch (e) {
console.error(e) console.error(e)
} }
// Hardcode pubkeys for testing
/*
this.pubkeys.add(
'c1415f950a1e3431de2bc5ee35144639e2f514cf158279abff9ed77d50118796'
)
this.pubkeys.add(
'8f69ac99b96f7c4ad58b98cc38fe5d35ce02daefae7d1609c797ce3b4f92f5fd'
)
*/
// stall ids S4hQgtTwiF5kGJZPbqMH9M jkCbdtkXeMjGBY3LBf8yn4
/*let naddr = nostr.nip19.naddrEncode({
identifier: '1234',
pubkey:
'c1415f950a1e3431de2bc5ee35144639e2f514cf158279abff9ed77d50118796',
kind: 30018,
relays: defaultRelays
})
console.log(naddr)
console.log(nostr.nip19.decode(naddr))
*/
let params = new URLSearchParams(window.location.search) let params = new URLSearchParams(window.location.search)
let merchant_pubkey = params.get('merchant_pubkey') let merchant_pubkey = params.get('merchant_pubkey')
let stall_id = params.get('stall_id') let stall_id = params.get('stall_id')
let product_id = params.get('product_id') let product_id = params.get('product_id')
console.log(merchant_pubkey, stall_id, product_id)
if (merchant_pubkey) {
await addPubkey(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)
})*/
}
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
if (stall_id) { if (stall_id) {
@ -142,11 +112,33 @@ const market = async () => {
this.activePage = 'stall' this.activePage = 'stall'
this.activeStall = stall_id this.activeStall = stall_id
} }
if (merchant_pubkey && !this.pubkeys.has(merchant_pubkey)) {
await LNbits.utils
.confirmDialog(
`We found a merchant pubkey in your request. Do you want to add it to the merchants list?`
)
.onOk(async () => {
await this.addPubkey(merchant_pubkey)
})
}
// Get notes from Nostr
await this.initNostr()
this.$q.loading.hide() this.$q.loading.hide()
}, },
methods: { methods: {
naddr() {
let naddr = nostr.nip19.naddrEncode({
identifier: '1234',
pubkey:
'c1415f950a1e3431de2bc5ee35144639e2f514cf158279abff9ed77d50118796',
kind: 30018,
relays: defaultRelays
})
console.log(naddr)
},
async initNostr() { async initNostr() {
this.$q.loading.show()
const pool = new nostr.SimplePool() const pool = new nostr.SimplePool()
let relays = Array.from(this.relays) let relays = Array.from(this.relays)
let products = new Map() let products = new Map()
@ -168,10 +160,10 @@ const market = async () => {
return return
} else if (e.kind == 30018) { } else if (e.kind == 30018) {
//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, categories: e.t}) products.set(e.d, {...e.content, id: e.d[0], categories: e.t})
} else if (e.kind == 30017) { } else if (e.kind == 30017) {
// 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, pubkey: e.pubkey}) stalls.set(e.d, {...e.content, id: e.d[0], pubkey: e.pubkey})
return return
} }
}) })
@ -182,13 +174,13 @@ const market = async () => {
this.products = Array.from(products.values()).map(obj => { this.products = Array.from(products.values()).map(obj => {
let stall = this.stalls.find(s => s.id == obj.stall_id) let stall = this.stalls.find(s => s.id == obj.stall_id)
obj.stallName = stall.name obj.stallName = stall.name
obj.images = [obj.image]
if (obj.currency != 'sat') { if (obj.currency != 'sat') {
obj.formatedPrice = this.getAmountFormated(obj.price, obj.currency) obj.formatedPrice = this.getAmountFormated(obj.price, obj.currency)
obj.priceInSats = this.getValueInSats(obj.price, obj.currency) obj.priceInSats = this.getValueInSats(obj.price, obj.currency)
} }
return obj return obj
}) })
pool.close(relays) pool.close(relays)
}, },
async getRates() { async getRates() {

View file

@ -140,7 +140,7 @@
</div> </div>
</div> </div>
<customer-stall <customer-stall
v-if="activeStall" v-if="!isLoading && activeStall"
:stall="stalls.find(stall => stall.id == activeStall)" :stall="stalls.find(stall => stall.id == activeStall)"
:products="filterProducts" :products="filterProducts"
:exchange-rates="exchangeRates" :exchange-rates="exchangeRates"
@ -163,6 +163,7 @@
<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 src="{{ url_for('nostrmarket_static', path='components/customer-stall/customer-stall.js') }}"></script>
<script src="{{ url_for('nostrmarket_static', path='components/product-detail/product-detail.js') }}"></script> <script src="{{ url_for('nostrmarket_static', path='components/product-detail/product-detail.js') }}"></script>
<script src="{{ url_for('nostrmarket_static', path='components/shopping-cart/shopping-cart.js') }}"></script>
<script src="{{ url_for('nostrmarket_static', path='js/market.js') }}"></script> <script src="{{ url_for('nostrmarket_static', path='js/market.js') }}"></script>
{% endblock %} {% endblock %}